dino-0.4.3/0000755000000000000000000000000014452563620011154 5ustar rootrootdino-0.4.3/.github/0000755000000000000000000000000014452563620012514 5ustar rootrootdino-0.4.3/.github/workflows/0000755000000000000000000000000014452563620014551 5ustar rootrootdino-0.4.3/.github/workflows/build.yml0000644000000000000000000000125214452563620016373 0ustar rootrootname: Build on: [pull_request, push] jobs: build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - run: sudo apt-get update - run: sudo apt-get remove libunwind-14-dev - run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev - run: ./configure --with-tests --with-libsignal-in-tree - run: make - run: build/xmpp-vala-test - run: build/signal-protocol-vala-test dino-0.4.3/.gitignore0000644000000000000000000000010414452563620013137 0ustar rootroot*.o build/ Makefile .vscode/ *.iml .idea .sqlite3 gschemas.compiled dino-0.4.3/.gitmodules0000644000000000000000000000025314452563620013331 0ustar rootroot[submodule "libsignal-protocol-c"] path = plugins/signal-protocol/libsignal-protocol-c url = https://github.com/WhisperSystems/libsignal-protocol-c.git branch = v2.3.3 dino-0.4.3/CMakeLists.txt0000644000000000000000000002277214452563620013726 0ustar rootrootcmake_minimum_required(VERSION 3.3) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) include(ComputeVersion) if (NOT VERSION_FOUND) project(Dino LANGUAGES C CXX) elseif (VERSION_IS_RELEASE) project(Dino VERSION ${VERSION_FULL} LANGUAGES C CXX) else () project(Dino LANGUAGES C CXX) set(PROJECT_VERSION ${VERSION_FULL}) endif () # Prepare Plugins set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp) foreach (plugin ${DEFAULT_PLUGINS}) if ("$CACHE{DINO_PLUGIN_ENABLED_${plugin}}" STREQUAL "") if (NOT DEFINED DINO_PLUGIN_ENABLED_${plugin}}) set(DINO_PLUGIN_ENABLED_${plugin} "yes" CACHE BOOL "Enable plugin ${plugin}") else () set(DINO_PLUGIN_ENABLED_${plugin} "${DINO_PLUGIN_ENABLED_${plugin}}" CACHE BOOL "Enable plugin ${plugin}" FORCE) endif () if (DINO_PLUGIN_ENABLED_${plugin}) message(STATUS "Enabled plugin: ${plugin}") else () message(STATUS "Disabled plugin: ${plugin}") endif () endif () endforeach (plugin) if (DISABLED_PLUGINS) foreach(plugin ${DISABLED_PLUGINS}) set(DINO_PLUGIN_ENABLED_${plugin} "no" CACHE BOOL "Enable plugin ${plugin}" FORCE) message(STATUS "Disabled plugin: ${plugin}") endforeach(plugin) endif (DISABLED_PLUGINS) if (ENABLED_PLUGINS) foreach(plugin ${ENABLED_PLUGINS}) set(DINO_PLUGIN_ENABLED_${plugin} "yes" CACHE BOOL "Enable plugin ${plugin}" FORCE) message(STATUS "Enabled plugin: ${plugin}") endforeach(plugin) endif (ENABLED_PLUGINS) set(PLUGINS "") get_cmake_property(all_variables VARIABLES) foreach (variable_name ${all_variables}) if (variable_name MATCHES "^DINO_PLUGIN_ENABLED_(.+)$" AND ${variable_name}) list(APPEND PLUGINS ${CMAKE_MATCH_1}) endif() endforeach () list(SORT PLUGINS) string(REPLACE ";" ", " PLUGINS_TEXT "${PLUGINS}") message(STATUS "Configuring Dino ${PROJECT_VERSION} with plugins: ${PLUGINS_TEXT}") # Prepare instal paths macro(set_path what val desc) if (NOT ${what}) unset(${what} CACHE) set(${what} ${val}) endif () if (NOT "${${what}}" STREQUAL "${_${what}_SET}") message(STATUS "${desc}: ${${what}}") set(_${what}_SET ${${what}} CACHE INTERNAL ${desc}) endif() endmacro(set_path) string(REGEX REPLACE "^liblib" "lib" LIBDIR_NAME "lib${LIB_SUFFIX}") set_path(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-independent files") set_path(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-dependent files") set_path(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" "Installation directory for read-only architecture-independent data") set_path(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" "Installation directory for user executables") set_path(DATA_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dino" "Installation directory for dino-specific data") set_path(APPDATA_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/metainfo" "Installation directory for .appdata.xml files") set_path(DESKTOP_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/applications" "Installation directory for .desktop files") set_path(SERVICE_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "Installation directory for .service files") set_path(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "Installation directory for icons") set_path(INCLUDE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/include" "Installation directory for C header files") set_path(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${LIBDIR_NAME}" "Installation directory for object code libraries") set_path(LOCALE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/locale" "Installation directory for locale files") set_path(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/dino/plugins" "Installation directory for dino plugin object code files") set_path(VAPI_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/vala/vapi" "Installation directory for Vala API files") set(TARGET_INSTALL LIBRARY DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) set(PLUGIN_INSTALL LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} RUNTIME DESTINATION ${PLUGIN_INSTALL_DIR}) include(CheckCCompilerFlag) include(CheckCSourceCompiles) macro(AddCFlagIfSupported list flag) string(REGEX REPLACE "[^a-z^A-Z^_^0-9]+" "_" flag_name ${flag}) check_c_compiler_flag(${flag} COMPILER_SUPPORTS${flag_name}) if (${COMPILER_SUPPORTS${flag_name}}) set(${list} "${${list}} ${flag}") endif () endmacro() if ("Ninja" STREQUAL ${CMAKE_GENERATOR}) AddCFlagIfSupported(CMAKE_C_FLAGS -fdiagnostics-color) endif () # Flags for all C files AddCFlagIfSupported(CMAKE_C_FLAGS -Wall) AddCFlagIfSupported(CMAKE_C_FLAGS -Wextra) AddCFlagIfSupported(CMAKE_C_FLAGS -Werror=format-security) AddCFlagIfSupported(CMAKE_C_FLAGS -Wno-duplicate-decl-specifier) AddCFlagIfSupported(CMAKE_C_FLAGS -fno-omit-frame-pointer) if (NOT VALA_WARN) set(VALA_WARN "conversion") endif () set(VALA_WARN "${VALA_WARN}" CACHE STRING "Which warnings to show when invoking C compiler on Vala compiler output") set_property(CACHE VALA_WARN PROPERTY STRINGS "all;unused;qualifier;conversion;deprecated;format;none") # Vala generates some unused stuff if (NOT ("all" IN_LIST VALA_WARN OR "unused" IN_LIST VALA_WARN)) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-but-set-variable) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-function) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-label) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-parameter) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-value) AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-variable) endif () if (NOT ("all" IN_LIST VALA_WARN OR "qualifier" IN_LIST VALA_WARN)) AddCFlagIfSupported(VALA_CFLAGS -Wno-discarded-qualifiers) AddCFlagIfSupported(VALA_CFLAGS -Wno-discarded-array-qualifiers) AddCFlagIfSupported(VALA_CFLAGS -Wno-incompatible-pointer-types-discards-qualifiers) endif () if (NOT ("all" IN_LIST VALA_WARN OR "deprecated" IN_LIST VALA_WARN)) AddCFlagIfSupported(VALA_CFLAGS -Wno-deprecated-declarations) endif () if (NOT ("all" IN_LIST VALA_WARN OR "format" IN_LIST VALA_WARN)) AddCFlagIfSupported(VALA_CFLAGS -Wno-missing-braces) endif () if (NOT ("all" IN_LIST VALA_WARN OR "conversion" IN_LIST VALA_WARN)) AddCFlagIfSupported(VALA_CFLAGS -Wno-int-conversion) AddCFlagIfSupported(VALA_CFLAGS -Wno-pointer-sign) AddCFlagIfSupported(VALA_CFLAGS -Wno-incompatible-pointer-types) endif () try_compile(__WITHOUT_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/LargeFileOffsets.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) if (NOT __WITHOUT_FILE_OFFSET_BITS_64) try_compile(__WITH_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/LargeFileOffsets.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64) if (__WITH_FILE_OFFSET_BITS_64) AddCFlagIfSupported(CMAKE_C_FLAGS -D_FILE_OFFSET_BITS=64) message(STATUS "Enabled large file support using _FILE_OFFSET_BITS=64") else (__WITH_FILE_OFFSET_BITS_64) message(STATUS "Large file support not available") endif (__WITH_FILE_OFFSET_BITS_64) unset(__WITH_FILE_OFFSET_BITS_64) endif (NOT __WITHOUT_FILE_OFFSET_BITS_64) unset(__WITHOUT_FILE_OFFSET_BITS_64) if ($ENV{USE_CCACHE}) # Configure CCache if available find_program(CCACHE_BIN ccache) mark_as_advanced(CCACHE_BIN) if (CCACHE_BIN) message(STATUS "Using ccache") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_BIN}) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_BIN}) else (CCACHE_BIN) message(STATUS "USE_CCACHE was set but ccache was not found") endif (CCACHE_BIN) endif ($ENV{USE_CCACHE}) if (NOT NO_DEBUG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} -g") endif (NOT NO_DEBUG) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(GLib_GLOBAL_VERSION 2.38) set(ICU_GLOBAL_VERSION 57) if (NOT VALA_EXECUTABLE) unset(VALA_EXECUTABLE CACHE) endif () find_package(Vala 0.34 REQUIRED) if (VALA_VERSION VERSION_GREATER "0.34.90" AND VALA_VERSION VERSION_LESS "0.36.1" OR # Due to a bug on 0.36.0 (and pre-releases), we need to disable FAST_VAPI VALA_VERSION VERSION_EQUAL "0.44.10" OR VALA_VERSION VERSION_EQUAL "0.46.4" OR VALA_VERSION VERSION_EQUAL "0.47.1" OR # See Dino issue #646 VALA_VERSION VERSION_EQUAL "0.40.21" OR VALA_VERSION VERSION_EQUAL "0.46.8" OR VALA_VERSION VERSION_EQUAL "0.48.4") # See Dino issue #816 set(DISABLE_FAST_VAPI yes) endif () include(${VALA_USE_FILE}) include(MultiFind) include(GlibCompileResourcesSupport) find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED) string(REGEX REPLACE "^([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" GLib_MAJOR_VERSION "${GLib_VERSION}") string(REGEX REPLACE "^[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" GLib_MINOR_VERSION "${GLib_VERSION}") math(EXPR GLib_LAST_RELEASE_MINOR_VERSION "${GLib_MINOR_VERSION} / 2 * 2") set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_MAJOR_VERSION}.${GLib_LAST_RELEASE_MINOR_VERSION}") add_subdirectory(qlite) add_subdirectory(xmpp-vala) add_subdirectory(libdino) add_subdirectory(main) add_subdirectory(crypto-vala) add_subdirectory(plugins) # uninstall target configure_file("${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake_uninstall.cmake COMMENT "Uninstall the project...") dino-0.4.3/LICENSE0000644000000000000000000010451514452563620012167 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . dino-0.4.3/README.md0000644000000000000000000000343314452563620012436 0ustar rootroot![Dino](https://dino.im/img/readme_header.svg) ======= ![screenshots](https://dino.im/img/screenshot-main.png) Installation ------------ Have a look at the [prebuilt packages](https://github.com/dino/dino/wiki/Distribution-Packages). Build ----- Make sure to install all [dependencies](https://github.com/dino/dino/wiki/Build#dependencies). ./configure make build/dino Resources --------- - Check out the [Dino website](https://dino.im). - Join our XMPP channel at `chat@dino.im`. - The [wiki](https://github.com/dino/dino/wiki) provides additional information. Contribute ---------- - Pull requests are welcome. [These](https://github.com/dino/dino/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) might be good first issues. Please discuss bigger changes in our channel first. - Look at [how to debug](https://github.com/dino/dino/wiki/Debugging) Dino before you report a bug. - Help [translating](https://github.com/dino/dino/wiki/Translations) Dino into your language. - Make a [donation](https://dino.im/#donate). License ------- Dino - Modern Jabber/XMPP Client using GTK+/Vala Copyright (C) 2016-2023 Dino contributors 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 . dino-0.4.3/VERSION0000644000000000000000000000001614452563620012221 0ustar rootrootRELEASE 0.4.3 dino-0.4.3/cmake/0000755000000000000000000000000014452563620012234 5ustar rootrootdino-0.4.3/cmake/BuildTargetScript.cmake0000644000000000000000000000444314452563620016636 0ustar rootroot# This file is used to be invoked at build time. It generates the needed # resource XML file. # Input variables that need to provided when invoking this script: # GXML_OUTPUT The output file path where to save the XML file. # GXML_COMPRESS_ALL Sets all COMPRESS flags in all resources in resource # list. # GXML_NO_COMPRESS_ALL Removes all COMPRESS flags in all resources in # resource list. # GXML_STRIPBLANKS_ALL Sets all STRIPBLANKS flags in all resources in # resource list. # GXML_NO_STRIPBLANKS_ALL Removes all STRIPBLANKS flags in all resources in # resource list. # GXML_TOPIXDATA_ALL Sets all TOPIXDATA flags i nall resources in resource # list. # GXML_NO_TOPIXDATA_ALL Removes all TOPIXDATA flags in all resources in # resource list. # GXML_PREFIX Overrides the resource prefix that is prepended to # each relative name in registered resources. # GXML_RESOURCES The list of resource files. Whether absolute or # relative path is equal. # Include the GENERATE_GXML() function. include(${CMAKE_CURRENT_LIST_DIR}/GenerateGXML.cmake) # Set flags to actual invocation flags. if(GXML_COMPRESS_ALL) set(GXML_COMPRESS_ALL COMPRESS_ALL) endif() if(GXML_NO_COMPRESS_ALL) set(GXML_NO_COMPRESS_ALL NO_COMPRESS_ALL) endif() if(GXML_STRIPBLANKS_ALL) set(GXML_STRIPBLANKS_ALL STRIPBLANKS_ALL) endif() if(GXML_NO_STRIPBLANKS_ALL) set(GXML_NO_STRIPBLANKS_ALL NO_STRIPBLANKS_ALL) endif() if(GXML_TOPIXDATA_ALL) set(GXML_TOPIXDATA_ALL TOPIXDATA_ALL) endif() if(GXML_NO_TOPIXDATA_ALL) set(GXML_NO_TOPIXDATA_ALL NO_TOPIXDATA_ALL) endif() # Replace " " with ";" to import the list over the command line. Otherwise # CMake would interprete the passed resources as a whole string. string(REPLACE " " ";" GXML_RESOURCES ${GXML_RESOURCES}) # Invoke the gresource XML generation function. generate_gxml(${GXML_OUTPUT} ${GXML_COMPRESS_ALL} ${GXML_NO_COMPRESS_ALL} ${GXML_STRIPBLANKS_ALL} ${GXML_NO_STRIPBLANKS_ALL} ${GXML_TOPIXDATA_ALL} ${GXML_NO_TOPIXDATA_ALL} PREFIX ${GXML_PREFIX} RESOURCES ${GXML_RESOURCES}) dino-0.4.3/cmake/CompileGResources.cmake0000644000000000000000000002404414452563620016634 0ustar rootrootinclude(CMakeParseArguments) # Path to this file. set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR}) # Compiles a gresource resource file from given resource files. Automatically # creates the XML controlling file. # The type of resource to generate (header, c-file or bundle) is automatically # determined from TARGET file ending, if no TYPE is explicitly specified. # The output file is stored in the provided variable "output". # "xml_out" contains the variable where to output the XML path. Can be used to # create custom targets or doing postprocessing. # If you want to use preprocessing, you need to manually check the existence # of the tools you use. This function doesn't check this for you, it just # generates the XML file. glib-compile-resources will then throw a # warning/error. function(COMPILE_GRESOURCES output xml_out) # Available options: # COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all # registered resources. # STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all # registered resources. # TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all # registered resources. set(CG_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL STRIPBLANKS_ALL NO_STRIPBLANKS_ALL TOPIXDATA_ALL NO_TOPIXDATA_ALL) # Available one value options: # TYPE Type of resource to create. Valid options are: # EMBED_C: A C-file that can be compiled with your project. # EMBED_H: A header that can be included into your project. # BUNDLE: Generates a resource bundle file that can be loaded # at runtime. # AUTO: Determine from target file ending. Need to specify # target argument. # PREFIX Overrides the resource prefix that is prepended to each # relative file name in registered resources. # SOURCE_DIR Overrides the resources base directory to search for resources. # Normally this is set to the source directory with that CMake # was invoked (CMAKE_SOURCE_DIR). # TARGET Overrides the name of the output file/-s. Normally the output # names from glib-compile-resources tool is taken. set(CG_ONEVALUEARGS TYPE PREFIX SOURCE_DIR TARGET) # Available multi-value options: # RESOURCES The list of resource files. Whether absolute or relative path is # equal, absolute paths are stripped down to relative ones. If the # absolute path is not inside the given base directory SOURCE_DIR # or CMAKE_SOURCE_DIR (if SOURCE_DIR is not overriden), this # function aborts. # OPTIONS Extra command line options passed to glib-compile-resources. set(CG_MULTIVALUEARGS RESOURCES OPTIONS) # Parse the arguments. cmake_parse_arguments(CG_ARG "${CG_OPTIONS}" "${CG_ONEVALUEARGS}" "${CG_MULTIVALUEARGS}" "${ARGN}") # Variable to store the double-quote (") string. Since escaping # double-quotes in strings is not possible we need a helper variable that # does this job for us. set(Q \") # Check invocation validity with the _UNPARSED_ARGUMENTS variable. # If other not recognized parameters were passed, throw error. if (CG_ARG_UNPARSED_ARGUMENTS) set(CG_WARNMSG "Invocation of COMPILE_GRESOURCES with unrecognized") set(CG_WARNMSG "${CG_WARNMSG} parameters. Parameters are:") set(CG_WARNMSG "${CG_WARNMSG} ${CG_ARG_UNPARSED_ARGUMENTS}.") message(WARNING ${CG_WARNMSG}) endif() # Check invocation validity depending on generation mode (EMBED_C, EMBED_H # or BUNDLE). if ("${CG_ARG_TYPE}" STREQUAL "EMBED_C") # EMBED_C mode, output compilable C-file. set(CG_GENERATE_COMMAND_LINE "--generate-source") set(CG_TARGET_FILE_ENDING "c") elseif ("${CG_ARG_TYPE}" STREQUAL "EMBED_H") # EMBED_H mode, output includable header file. set(CG_GENERATE_COMMAND_LINE "--generate-header") set(CG_TARGET_FILE_ENDING "h") elseif ("${CG_ARG_TYPE}" STREQUAL "BUNDLE") # BUNDLE mode, output resource bundle. Don't do anything since # glib-compile-resources outputs a bundle when not specifying # something else. set(CG_TARGET_FILE_ENDING "gresource") else() # Everything else is AUTO mode, determine from target file ending. if (CG_ARG_TARGET) set(CG_GENERATE_COMMAND_LINE "--generate") else() set(CG_ERRMSG "AUTO mode given, but no target specified. Can't") set(CG_ERRMSG "${CG_ERRMSG} determine output type. In function") set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.") message(FATAL_ERROR ${CG_ERRMSG}) endif() endif() # Check flag validity. if (CG_ARG_COMPRESS_ALL AND CG_ARG_NO_COMPRESS_ALL) set(CG_ERRMSG "COMPRESS_ALL and NO_COMPRESS_ALL simultaneously set. In") set(CG_ERRMSG "${CG_ERRMSG} function COMPILE_GRESOURCES.") message(FATAL_ERROR ${CG_ERRMSG}) endif() if (CG_ARG_STRIPBLANKS_ALL AND CG_ARG_NO_STRIPBLANKS_ALL) set(CG_ERRMSG "STRIPBLANKS_ALL and NO_STRIPBLANKS_ALL simultaneously") set(CG_ERRMSG "${CG_ERRMSG} set. In function COMPILE_GRESOURCES.") message(FATAL_ERROR ${CG_ERRMSG}) endif() if (CG_ARG_TOPIXDATA_ALL AND CG_ARG_NO_TOPIXDATA_ALL) set(CG_ERRMSG "TOPIXDATA_ALL and NO_TOPIXDATA_ALL simultaneously set.") set(CG_ERRMSG "${CG_ERRMSG} In function COMPILE_GRESOURCES.") message(FATAL_ERROR ${CG_ERRMSG}) endif() # Check if there are any resources. if (NOT CG_ARG_RESOURCES) set(CG_ERRMSG "No resource files to process. In function") set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.") message(FATAL_ERROR ${CG_ERRMSG}) endif() # Extract all dependencies for targets from resource list. foreach(res ${CG_ARG_RESOURCES}) if (NOT(("${res}" STREQUAL "COMPRESS") OR ("${res}" STREQUAL "STRIPBLANKS") OR ("${res}" STREQUAL "TOPIXDATA"))) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}" COMMAND ${CMAKE_COMMAND} -E copy "${CG_ARG_SOURCE_DIR}/${res}" "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}" MAIN_DEPENDENCY "${CG_ARG_SOURCE_DIR}/${res}") list(APPEND CG_RESOURCES_DEPENDENCIES "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}") endif() endforeach() # Construct .gresource.xml path. set(CG_XML_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources/.gresource.xml") # Generate gresources XML target. list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_OUTPUT=${Q}${CG_XML_FILE_PATH}${Q}") if(CG_ARG_COMPRESS_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_COMPRESS_ALL") endif() if(CG_ARG_NO_COMPRESS_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_COMPRESS_ALL") endif() if(CG_ARG_STRPIBLANKS_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_STRIPBLANKS_ALL") endif() if(CG_ARG_NO_STRIPBLANKS_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_STRIPBLANKS_ALL") endif() if(CG_ARG_TOPIXDATA_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_TOPIXDATA_ALL") endif() if(CG_ARG_NO_TOPIXDATA_ALL) list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_TOPIXDATA_ALL") endif() list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_PREFIX=${Q}${CG_ARG_PREFIX}${Q}") list(APPEND CG_CMAKE_SCRIPT_ARGS "-D") list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_RESOURCES=${Q}${CG_ARG_RESOURCES}${Q}") list(APPEND CG_CMAKE_SCRIPT_ARGS "-P") list(APPEND CG_CMAKE_SCRIPT_ARGS "${Q}${GCR_CMAKE_MACRO_DIR}/BuildTargetScript.cmake${Q}") get_filename_component(CG_XML_FILE_PATH_ONLY_NAME "${CG_XML_FILE_PATH}" NAME) set(CG_XML_CUSTOM_COMMAND_COMMENT "Creating gresources XML file (${CG_XML_FILE_PATH_ONLY_NAME})") add_custom_command(OUTPUT ${CG_XML_FILE_PATH} COMMAND ${CMAKE_COMMAND} ARGS ${CG_CMAKE_SCRIPT_ARGS} DEPENDS ${CG_RESOURCES_DEPENDENCIES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT ${CG_XML_CUSTOM_COMMAND_COMMENT}) # Create target manually if not set (to make sure glib-compile-resources # doesn't change behaviour with it's naming standards). if (NOT CG_ARG_TARGET) set(CG_ARG_TARGET "${CMAKE_CURRENT_BINARY_DIR}/resources") set(CG_ARG_TARGET "${CG_ARG_TARGET}.${CG_TARGET_FILE_ENDING}") endif() # Create source directory automatically if not set. if (NOT CG_ARG_SOURCE_DIR) set(CG_ARG_SOURCE_DIR "${CMAKE_SOURCE_DIR}") endif() # Add compilation target for resources. add_custom_command(OUTPUT ${CG_ARG_TARGET} COMMAND ${GLIB_COMPILE_RESOURCES_EXECUTABLE} ARGS ${OPTIONS} "--target=${Q}${CG_ARG_TARGET}${Q}" "--sourcedir=${Q}${CG_ARG_SOURCE_DIR}${Q}" ${CG_GENERATE_COMMAND_LINE} ${CG_XML_FILE_PATH} MAIN_DEPENDENCY ${CG_XML_FILE_PATH} DEPENDS ${CG_RESOURCES_DEPENDENCIES} WORKING_DIRECTORY ${CMAKE_BUILD_DIR}) # Set output and XML_OUT to parent scope. set(${xml_out} ${CG_XML_FILE_PATH} PARENT_SCOPE) set(${output} ${CG_ARG_TARGET} PARENT_SCOPE) endfunction() dino-0.4.3/cmake/ComputeVersion.cmake0000644000000000000000000000753714452563620016234 0ustar rootrootinclude(CMakeParseArguments) function(_compute_version_from_file) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/VERSION) if (NOT EXISTS ${CMAKE_SOURCE_DIR}/VERSION) set(VERSION_FOUND 0 PARENT_SCOPE) return() endif () file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION VERSION_FILE) string(REPLACE " " ";" VERSION_FILE "${VERSION_FILE}") cmake_parse_arguments(VERSION_FILE "" "RELEASE;PRERELEASE" "" ${VERSION_FILE}) if (DEFINED VERSION_FILE_RELEASE) string(STRIP "${VERSION_FILE_RELEASE}" VERSION_FILE_RELEASE) set(VERSION_IS_RELEASE 1 PARENT_SCOPE) set(VERSION_FULL "${VERSION_FILE_RELEASE}" PARENT_SCOPE) set(VERSION_FOUND 1 PARENT_SCOPE) elseif (DEFINED VERSION_FILE_PRERELEASE) string(STRIP "${VERSION_FILE_PRERELEASE}" VERSION_FILE_PRERELEASE) set(VERSION_IS_RELEASE 0 PARENT_SCOPE) set(VERSION_FULL "${VERSION_FILE_PRERELEASE}" PARENT_SCOPE) set(VERSION_FOUND 1 PARENT_SCOPE) else () set(VERSION_FOUND 0 PARENT_SCOPE) endif () endfunction(_compute_version_from_file) function(_compute_version_from_git) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/.git) if (NOT GIT_EXECUTABLE) find_package(Git QUIET) if (NOT GIT_FOUND) return() endif () endif (NOT GIT_EXECUTABLE) # Git tag execute_process( COMMAND "${GIT_EXECUTABLE}" describe --tags --abbrev=0 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE git_result OUTPUT_VARIABLE git_tag ERROR_VARIABLE git_error OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) if (NOT git_result EQUAL 0) return() endif (NOT git_result EQUAL 0) if (git_tag MATCHES "^v?([0-9]+[.]?[0-9]*[.]?[0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$") set(VERSION_LAST_RELEASE "${CMAKE_MATCH_1}") else () return() endif () # Git describe execute_process( COMMAND "${GIT_EXECUTABLE}" describe --tags WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE git_result OUTPUT_VARIABLE git_describe ERROR_VARIABLE git_error OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) if (NOT git_result EQUAL 0) return() endif (NOT git_result EQUAL 0) if ("${git_tag}" STREQUAL "${git_describe}") set(VERSION_IS_RELEASE 1) else () set(VERSION_IS_RELEASE 0) if (git_describe MATCHES "-([0-9]+)-g([0-9a-f]+)$") set(VERSION_TAG_OFFSET "${CMAKE_MATCH_1}") set(VERSION_COMMIT_HASH "${CMAKE_MATCH_2}") endif () execute_process( COMMAND "${GIT_EXECUTABLE}" show --format=%cd --date=format:%Y%m%d -s WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE git_result OUTPUT_VARIABLE git_time ERROR_VARIABLE git_error OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) if (NOT git_result EQUAL 0) return() endif (NOT git_result EQUAL 0) set(VERSION_COMMIT_DATE "${git_time}") endif () if (NOT VERSION_IS_RELEASE) set(VERSION_SUFFIX "~git${VERSION_TAG_OFFSET}.${VERSION_COMMIT_DATE}.${VERSION_COMMIT_HASH}") else (NOT VERSION_IS_RELEASE) set(VERSION_SUFFIX "") endif (NOT VERSION_IS_RELEASE) set(VERSION_IS_RELEASE ${VERSION_IS_RELEASE} PARENT_SCOPE) set(VERSION_FULL "${VERSION_LAST_RELEASE}${VERSION_SUFFIX}" PARENT_SCOPE) set(VERSION_FOUND 1 PARENT_SCOPE) endfunction(_compute_version_from_git) _compute_version_from_file() if (NOT VERSION_FOUND) _compute_version_from_git() endif (NOT VERSION_FOUND)dino-0.4.3/cmake/FindATK.cmake0000644000000000000000000000267014452563620014463 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(ATK PKG_CONFIG_NAME atk LIB_NAMES atk-1.0 INCLUDE_NAMES atk/atk.h INCLUDE_DIR_SUFFIXES atk-1.0 atk-1.0/include DEPENDS GObject ) if(ATK_FOUND AND NOT ATK_VERSION) find_file(ATK_VERSION_HEADER "atk/atkversion.h" HINTS ${ATK_INCLUDE_DIRS}) mark_as_advanced(ATK_VERSION_HEADER) if(ATK_VERSION_HEADER) file(STRINGS "${ATK_VERSION_HEADER}" ATK_MAJOR_VERSION REGEX "^#define ATK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define ATK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MAJOR_VERSION "${ATK_MAJOR_VERSION}") file(STRINGS "${ATK_VERSION_HEADER}" ATK_MINOR_VERSION REGEX "^#define ATK_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define ATK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MINOR_VERSION "${ATK_MINOR_VERSION}") file(STRINGS "${ATK_VERSION_HEADER}" ATK_MICRO_VERSION REGEX "^#define ATK_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define ATK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MICRO_VERSION "${ATK_MICRO_VERSION}") set(ATK_VERSION "${ATK_MAJOR_VERSION}.${ATK_MINOR_VERSION}.${ATK_MICRO_VERSION}") unset(ATK_MAJOR_VERSION) unset(ATK_MINOR_VERSION) unset(ATK_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ATK REQUIRED_VARS ATK_LIBRARY VERSION_VAR ATK_VERSION)dino-0.4.3/cmake/FindAdwaita.cmake0000644000000000000000000000047014452563620015412 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Adwaita PKG_CONFIG_NAME libadwaita-1 LIB_NAMES libadwaita-1 INCLUDE_NAMES adwaita.h ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Adwaita REQUIRED_VARS Adwaita_LIBRARY VERSION_VAR Adwaita_VERSION) dino-0.4.3/cmake/FindCairo.cmake0000644000000000000000000000274314452563620015102 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Cairo PKG_CONFIG_NAME cairo LIB_NAMES cairo INCLUDE_NAMES cairo.h INCLUDE_DIR_SUFFIXES cairo cairo/include ) if(Cairo_FOUND AND NOT Cairo_VERSION) find_file(Cairo_VERSION_HEADER "cairo-version.h" HINTS ${Cairo_INCLUDE_DIRS}) mark_as_advanced(Cairo_VERSION_HEADER) if(Cairo_VERSION_HEADER) file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MAJOR_VERSION REGEX "^#define CAIRO_VERSION_MAJOR +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define CAIRO_VERSION_MAJOR \\(?([0-9]+)\\)?$" "\\1" Cairo_MAJOR_VERSION "${Cairo_MAJOR_VERSION}") file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MINOR_VERSION REGEX "^#define CAIRO_VERSION_MINOR +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define CAIRO_VERSION_MINOR \\(?([0-9]+)\\)?$" "\\1" Cairo_MINOR_VERSION "${Cairo_MINOR_VERSION}") file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MICRO_VERSION REGEX "^#define CAIRO_VERSION_MICRO +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define CAIRO_VERSION_MICRO \\(?([0-9]+)\\)?$" "\\1" Cairo_MICRO_VERSION "${Cairo_MICRO_VERSION}") set(Cairo_VERSION "${Cairo_MAJOR_VERSION}.${Cairo_MINOR_VERSION}.${Cairo_MICRO_VERSION}") unset(Cairo_MAJOR_VERSION) unset(Cairo_MINOR_VERSION) unset(Cairo_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Cairo REQUIRED_VARS Cairo_LIBRARY VERSION_VAR Cairo_VERSION)dino-0.4.3/cmake/FindCanberra.cmake0000644000000000000000000000042314452563620015553 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Canberra PKG_CONFIG_NAME libcanberra LIB_NAMES canberra INCLUDE_NAMES canberra.h ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Canberra REQUIRED_VARS Canberra_LIBRARY) dino-0.4.3/cmake/FindGCrypt.cmake0000644000000000000000000000046614452563620015255 0ustar rootrootinclude(PkgConfigWithFallbackOnConfigScript) find_pkg_config_with_fallback_on_config_script(GCrypt PKG_CONFIG_NAME libgcrypt CONFIG_SCRIPT_NAME libgcrypt ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GCrypt REQUIRED_VARS GCrypt_LIBRARY VERSION_VAR GCrypt_VERSION) dino-0.4.3/cmake/FindGDK3.cmake0000644000000000000000000000333414452563620014532 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GDK3 PKG_CONFIG_NAME gdk-3.0 LIB_NAMES gdk-3 INCLUDE_NAMES gdk/gdk.h INCLUDE_DIR_SUFFIXES gtk-3.0 gtk-3.0/include gtk+-3.0 gtk+-3.0/include DEPENDS Pango Cairo GDKPixbuf2 ) if(GDK3_FOUND AND NOT GDK3_VERSION) find_file(GDK3_VERSION_HEADER "gdk/gdkversionmacros.h" HINTS ${GDK3_INCLUDE_DIRS}) mark_as_advanced(GDK3_VERSION_HEADER) if(GDK3_VERSION_HEADER) file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MAJOR_VERSION REGEX "^#define GDK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MAJOR_VERSION "${GDK3_MAJOR_VERSION}") file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MINOR_VERSION REGEX "^#define GDK_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MINOR_VERSION "${GDK3_MINOR_VERSION}") file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MICRO_VERSION REGEX "^#define GDK_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MICRO_VERSION "${GDK3_MICRO_VERSION}") set(GDK3_VERSION "${GDK3_MAJOR_VERSION}.${GDK3_MINOR_VERSION}.${GDK3_MICRO_VERSION}") unset(GDK3_MAJOR_VERSION) unset(GDK3_MINOR_VERSION) unset(GDK3_MICRO_VERSION) endif() endif() if (GDK3_FOUND) find_file(GDK3_WITH_X11 "gdk/gdkx.h" HINTS ${GDK3_INCLUDE_DIRS}) if (GDK3_WITH_X11) set(GDK3_WITH_X11 yes CACHE INTERNAL "Does GDK3 support X11") endif (GDK3_WITH_X11) endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GDK3 REQUIRED_VARS GDK3_LIBRARY VERSION_VAR GDK3_VERSION)dino-0.4.3/cmake/FindGDK4.cmake0000644000000000000000000000333414452563620014533 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GDK4 PKG_CONFIG_NAME gdk-4.0 LIB_NAMES gdk-4 INCLUDE_NAMES gdk/gdk.h INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include DEPENDS Pango Cairo GDKPixbuf2 ) if(GDK4_FOUND AND NOT GDK4_VERSION) find_file(GDK4_VERSION_HEADER "gdk/gdkversionmacros.h" HINTS ${GDK4_INCLUDE_DIRS}) mark_as_advanced(GDK4_VERSION_HEADER) if(GDK4_VERSION_HEADER) file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MAJOR_VERSION REGEX "^#define GDK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MAJOR_VERSION "${GDK4_MAJOR_VERSION}") file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MINOR_VERSION REGEX "^#define GDK_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MINOR_VERSION "${GDK4_MINOR_VERSION}") file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MICRO_VERSION REGEX "^#define GDK_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GDK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MICRO_VERSION "${GDK4_MICRO_VERSION}") set(GDK4_VERSION "${GDK4_MAJOR_VERSION}.${GDK4_MINOR_VERSION}.${GDK4_MICRO_VERSION}") unset(GDK4_MAJOR_VERSION) unset(GDK4_MINOR_VERSION) unset(GDK4_MICRO_VERSION) endif() endif() if (GDK4_FOUND) find_file(GDK4_WITH_X11 "gdk/gdkx.h" HINTS ${GDK4_INCLUDE_DIRS}) if (GDK4_WITH_X11) set(GDK4_WITH_X11 yes CACHE INTERNAL "Does GDK4 support X11") endif (GDK4_WITH_X11) endif () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GDK4 REQUIRED_VARS GDK4_LIBRARY VERSION_VAR GDK4_VERSION)dino-0.4.3/cmake/FindGDKPixbuf2.cmake0000644000000000000000000000170414452563620015706 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GDKPixbuf2 PKG_CONFIG_NAME gdk-pixbuf-2.0 LIB_NAMES gdk_pixbuf-2.0 INCLUDE_NAMES gdk-pixbuf/gdk-pixbuf.h INCLUDE_DIR_SUFFIXES gdk-pixbuf-2.0 gdk-pixbuf-2.0/include DEPENDS GLib ) if(GDKPixbuf2_FOUND AND NOT GDKPixbuf2_VERSION) find_file(GDKPixbuf2_FEATURES_HEADER "gdk-pixbuf/gdk-pixbuf-features.h" HINTS ${GDKPixbuf2_INCLUDE_DIRS}) mark_as_advanced(GDKPixbuf2_FEATURES_HEADER) if(GDKPixbuf2_FEATURES_HEADER) file(STRINGS "${GDKPixbuf2_FEATURES_HEADER}" GDKPixbuf2_VERSION REGEX "^#define GDK_PIXBUF_VERSION \\\"[^\\\"]+\\\"") string(REGEX REPLACE "^#define GDK_PIXBUF_VERSION \\\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\\\"$" "\\1.\\2.\\3" GDKPixbuf2_VERSION "${GDKPixbuf2_VERSION}") endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GDKPixbuf2 REQUIRED_VARS GDKPixbuf2_LIBRARY VERSION_VAR GDKPixbuf2_VERSION)dino-0.4.3/cmake/FindGIO.cmake0000644000000000000000000000073614452563620014463 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GIO PKG_CONFIG_NAME gio-2.0 LIB_NAMES gio-2.0 INCLUDE_NAMES gio/gio.h INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include DEPENDS GObject ) if(GIO_FOUND AND NOT GIO_VERSION) find_package(GLib ${GLib_GLOBAL_VERSION}) set(GIO_VERSION ${GLib_VERSION}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GIO REQUIRED_VARS GIO_LIBRARY VERSION_VAR GIO_VERSION)dino-0.4.3/cmake/FindGLib.cmake0000644000000000000000000000305714452563620014661 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GLib PKG_CONFIG_NAME glib-2.0 LIB_NAMES glib-2.0 INCLUDE_NAMES glib.h glibconfig.h INCLUDE_DIR_HINTS ${CMAKE_LIBRARY_PATH} ${CMAKE_SYSTEM_LIBRARY_PATH} INCLUDE_DIR_PATHS ${CMAKE_PREFIX_PATH}/lib64 ${CMAKE_PREFIX_PATH}/lib INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include ) if(GLib_FOUND AND NOT GLib_VERSION) find_file(GLib_CONFIG_HEADER "glibconfig.h" HINTS ${GLib_INCLUDE_DIRS}) mark_as_advanced(GLib_CONFIG_HEADER) if(GLib_CONFIG_HEADER) file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MAJOR_VERSION REGEX "^#define GLIB_MAJOR_VERSION +([0-9]+)") string(REGEX REPLACE "^#define GLIB_MAJOR_VERSION ([0-9]+)$" "\\1" GLib_MAJOR_VERSION "${GLib_MAJOR_VERSION}") file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MINOR_VERSION REGEX "^#define GLIB_MINOR_VERSION +([0-9]+)") string(REGEX REPLACE "^#define GLIB_MINOR_VERSION ([0-9]+)$" "\\1" GLib_MINOR_VERSION "${GLib_MINOR_VERSION}") file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MICRO_VERSION REGEX "^#define GLIB_MICRO_VERSION +([0-9]+)") string(REGEX REPLACE "^#define GLIB_MICRO_VERSION ([0-9]+)$" "\\1" GLib_MICRO_VERSION "${GLib_MICRO_VERSION}") set(GLib_VERSION "${GLib_MAJOR_VERSION}.${GLib_MINOR_VERSION}.${GLib_MICRO_VERSION}") unset(GLib_MAJOR_VERSION) unset(GLib_MINOR_VERSION) unset(GLib_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GLib REQUIRED_VARS GLib_LIBRARY VERSION_VAR GLib_VERSION)dino-0.4.3/cmake/FindGModule.cmake0000644000000000000000000000101214452563620015365 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GModule PKG_CONFIG_NAME gmodule-2.0 LIB_NAMES gmodule-2.0 INCLUDE_NAMES gmodule.h INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include DEPENDS GLib ) if(GModule_FOUND AND NOT GModule_VERSION) # TODO find_package(GLib ${GLib_GLOBAL_VERSION}) set(GModule_VERSION ${GLib_VERSION}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GModule REQUIRED_VARS GModule_LIBRARY VERSION_VAR GModule_VERSION)dino-0.4.3/cmake/FindGObject.cmake0000644000000000000000000000102214452563620015347 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GObject PKG_CONFIG_NAME gobject-2.0 LIB_NAMES gobject-2.0 INCLUDE_NAMES gobject/gobject.h INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include DEPENDS GLib ) if(GObject_FOUND AND NOT GObject_VERSION) # TODO find_package(GLib ${GLib_GLOBAL_VERSION}) set(GObject_VERSION ${GLib_VERSION}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GObject REQUIRED_VARS GObject_LIBRARY VERSION_VAR GObject_VERSION)dino-0.4.3/cmake/FindGPGME.cmake0000644000000000000000000000045214452563620014677 0ustar rootrootinclude(PkgConfigWithFallbackOnConfigScript) find_pkg_config_with_fallback_on_config_script(GPGME PKG_CONFIG_NAME gpgme CONFIG_SCRIPT_NAME gpgme ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GPGME REQUIRED_VARS GPGME_LIBRARY VERSION_VAR GPGME_VERSION) dino-0.4.3/cmake/FindGTK3.cmake0000644000000000000000000000276414452563620014560 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GTK3 PKG_CONFIG_NAME gtk+-3.0 LIB_NAMES gtk-3 INCLUDE_NAMES gtk/gtk.h INCLUDE_DIR_SUFFIXES gtk-3.0 gtk-3.0/include gtk+-3.0 gtk+-3.0/include DEPENDS GDK3 ATK ) if(GTK3_FOUND AND NOT GTK3_VERSION) find_file(GTK3_VERSION_HEADER "gtk/gtkversion.h" HINTS ${GTK3_INCLUDE_DIRS}) mark_as_advanced(GTK3_VERSION_HEADER) if(GTK3_VERSION_HEADER) file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MAJOR_VERSION REGEX "^#define GTK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MAJOR_VERSION "${GTK3_MAJOR_VERSION}") file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MINOR_VERSION REGEX "^#define GTK_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MINOR_VERSION "${GTK3_MINOR_VERSION}") file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MICRO_VERSION REGEX "^#define GTK_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MICRO_VERSION "${GTK3_MICRO_VERSION}") set(GTK3_VERSION "${GTK3_MAJOR_VERSION}.${GTK3_MINOR_VERSION}.${GTK3_MICRO_VERSION}") unset(GTK3_MAJOR_VERSION) unset(GTK3_MINOR_VERSION) unset(GTK3_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GTK3 REQUIRED_VARS GTK3_LIBRARY VERSION_VAR GTK3_VERSION) dino-0.4.3/cmake/FindGTK4.cmake0000644000000000000000000000275514452563620014561 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GTK4 PKG_CONFIG_NAME gtk4 LIB_NAMES gtk-4 INCLUDE_NAMES gtk/gtk.h INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include gtk4 gtk4/include ) if(GTK4_FOUND AND NOT GTK4_VERSION) find_file(GTK4_VERSION_HEADER "gtk/gtkversion.h" HINTS ${GTK4_INCLUDE_DIRS}) mark_as_advanced(GTK4_VERSION_HEADER) if(GTK4_VERSION_HEADER) file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MAJOR_VERSION REGEX "^#define GTK_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MAJOR_VERSION "${GTK4_MAJOR_VERSION}") file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MINOR_VERSION REGEX "^#define GTK_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MINOR_VERSION "${GTK4_MINOR_VERSION}") file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MICRO_VERSION REGEX "^#define GTK_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define GTK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MICRO_VERSION "${GTK4_MICRO_VERSION}") set(GTK4_VERSION "${GTK4_MAJOR_VERSION}.${GTK4_MINOR_VERSION}.${GTK4_MICRO_VERSION}") unset(GTK4_MAJOR_VERSION) unset(GTK4_MINOR_VERSION) unset(GTK4_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GTK4 REQUIRED_VARS GTK4_LIBRARY VERSION_VAR GTK4_VERSION) dino-0.4.3/cmake/FindGee.cmake0000644000000000000000000000053214452563620014537 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Gee PKG_CONFIG_NAME gee-0.8 LIB_NAMES gee-0.8 INCLUDE_NAMES gee.h INCLUDE_DIR_SUFFIXES gee-0.8 gee-0.8/include DEPENDS GObject ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Gee REQUIRED_VARS Gee_LIBRARY VERSION_VAR Gee_VERSION)dino-0.4.3/cmake/FindGettext.cmake0000644000000000000000000000160514452563620015465 0ustar rootrootfind_program(XGETTEXT_EXECUTABLE xgettext) find_program(MSGMERGE_EXECUTABLE msgmerge) find_program(MSGFMT_EXECUTABLE msgfmt) find_program(MSGCAT_EXECUTABLE msgcat) mark_as_advanced(XGETTEXT_EXECUTABLE MSGMERGE_EXECUTABLE MSGFMT_EXECUTABLE MSGCAT_EXECUTABLE) if(XGETTEXT_EXECUTABLE) execute_process(COMMAND ${XGETTEXT_EXECUTABLE} "--version" OUTPUT_VARIABLE Gettext_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX REPLACE "xgettext \\(GNU gettext-tools\\) ([0-9\\.]*).*" "\\1" Gettext_VERSION "${Gettext_VERSION}") endif(XGETTEXT_EXECUTABLE) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Gettext FOUND_VAR Gettext_FOUND REQUIRED_VARS XGETTEXT_EXECUTABLE MSGMERGE_EXECUTABLE MSGFMT_EXECUTABLE MSGCAT_EXECUTABLE VERSION_VAR Gettext_VERSION) set(GETTEXT_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseGettext.cmake")dino-0.4.3/cmake/FindGnuTLS.cmake0000644000000000000000000000055114452563620015154 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GnuTLS PKG_CONFIG_NAME gnutls LIB_NAMES gnutls INCLUDE_NAMES gnutls/gnutls.h INCLUDE_DIR_SUFFIXES gnutls gnutls/include DEPENDS GLib ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GnuTLS REQUIRED_VARS GnuTLS_LIBRARY VERSION_VAR GnuTLS_VERSION)dino-0.4.3/cmake/FindGst.cmake0000644000000000000000000000054314452563620014576 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Gst PKG_CONFIG_NAME gstreamer-1.0 LIB_NAMES gstreamer-1.0 INCLUDE_NAMES gst/gst.h INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Gst REQUIRED_VARS Gst_LIBRARY VERSION_VAR Gst_VERSION) dino-0.4.3/cmake/FindGstApp.cmake0000644000000000000000000000071414452563620015237 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GstApp PKG_CONFIG_NAME gstreamer-app-1.0 LIB_NAMES gstapp LIB_DIR_HINTS gstreamer-1.0 INCLUDE_NAMES gst/app/app.h INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-app-1.0 gstreamer-app-1.0/include DEPENDS Gst ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GstApp REQUIRED_VARS GstApp_LIBRARY VERSION_VAR GstApp_VERSION) dino-0.4.3/cmake/FindGstAudio.cmake0000644000000000000000000000074014452563620015557 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GstAudio PKG_CONFIG_NAME gstreamer-audio-1.0 LIB_NAMES gstaudio LIB_DIR_HINTS gstreamer-1.0 INCLUDE_NAMES gst/audio/audio.h INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-audio-1.0 gstreamer-audio-1.0/include DEPENDS Gst ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GstAudio REQUIRED_VARS GstAudio_LIBRARY VERSION_VAR GstAudio_VERSION) dino-0.4.3/cmake/FindGstRtp.cmake0000644000000000000000000000107214452563620015262 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GstRtp PKG_CONFIG_NAME gstreamer-rtp-1.0 LIB_NAMES gstrtp LIB_DIR_HINTS gstreamer-1.0 INCLUDE_NAMES gst/rtp/rtp.h INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-rtp-1.0 gstreamer-rtp-1.0/include DEPENDS Gst ) if(GstRtp_FOUND AND NOT GstRtp_VERSION) find_package(Gst) set(GstRtp_VERSION ${Gst_VERSION}) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GstRtp REQUIRED_VARS GstRtp_LIBRARY VERSION_VAR GstRtp_VERSION) dino-0.4.3/cmake/FindGstVideo.cmake0000644000000000000000000000074014452563620015564 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(GstVideo PKG_CONFIG_NAME gstreamer-video-1.0 LIB_NAMES gstvideo LIB_DIR_HINTS gstreamer-1.0 INCLUDE_NAMES gst/video/video.h INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-video-1.0 gstreamer-video-1.0/include DEPENDS Gst ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GstVideo REQUIRED_VARS GstVideo_LIBRARY VERSION_VAR GstVideo_VERSION) dino-0.4.3/cmake/FindICU.cmake0000644000000000000000000000045014452563620014456 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(ICU PKG_CONFIG_NAME icu-uc LIB_NAMES icuuc icudata INCLUDE_NAMES unicode/umachine.h ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ICU REQUIRED_VARS ICU_LIBRARY VERSION_VAR ICU_VERSION) dino-0.4.3/cmake/FindNice.cmake0000644000000000000000000000052014452563620014712 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Nice PKG_CONFIG_NAME nice LIB_NAMES nice INCLUDE_NAMES nice.h INCLUDE_DIR_SUFFIXES nice nice/include DEPENDS GIO ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Nice REQUIRED_VARS Nice_LIBRARY VERSION_VAR Nice_VERSION) dino-0.4.3/cmake/FindPango.cmake0000644000000000000000000000306114452563620015103 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Pango PKG_CONFIG_NAME pango LIB_NAMES pango-1.0 INCLUDE_NAMES pango/pango.h INCLUDE_DIR_SUFFIXES pango-1.0 pango-1.0/include DEPENDS GObject ) if(Pango_FOUND AND NOT Pango_VERSION) find_file(Pango_FEATURES_HEADER "pango/pango-features.h" HINTS ${Pango_INCLUDE_DIRS}) mark_as_advanced(Pango_FEATURES_HEADER) if(Pango_FEATURES_HEADER) file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MAJOR_VERSION REGEX "^#define PANGO_VERSION_MAJOR +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define PANGO_VERSION_MAJOR \\(?([0-9]+)\\)?$" "\\1" Pango_MAJOR_VERSION "${Pango_MAJOR_VERSION}") file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MINOR_VERSION REGEX "^#define PANGO_VERSION_MINOR +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define PANGO_VERSION_MINOR \\(?([0-9]+)\\)?$" "\\1" Pango_MINOR_VERSION "${Pango_MINOR_VERSION}") file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MICRO_VERSION REGEX "^#define PANGO_VERSION_MICRO +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define PANGO_VERSION_MICRO \\(?([0-9]+)\\)?$" "\\1" Pango_MICRO_VERSION "${Pango_MICRO_VERSION}") set(Pango_VERSION "${Pango_MAJOR_VERSION}.${Pango_MINOR_VERSION}.${Pango_MICRO_VERSION}") unset(Pango_MAJOR_VERSION) unset(Pango_MINOR_VERSION) unset(Pango_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Pango FOUND_VAR Pango_FOUND REQUIRED_VARS Pango_LIBRARY VERSION_VAR Pango_VERSION )dino-0.4.3/cmake/FindQrencode.cmake0000644000000000000000000000046414452563620015603 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Qrencode PKG_CONFIG_NAME libqrencode LIB_NAMES qrencode INCLUDE_NAMES qrencode.h ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Qrencode REQUIRED_VARS Qrencode_LIBRARY VERSION_VAR Qrencode_VERSION) dino-0.4.3/cmake/FindSQLite3.cmake0000644000000000000000000000135514452563620015267 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(SQLite3 PKG_CONFIG_NAME sqlite3 LIB_NAMES sqlite3 INCLUDE_NAMES sqlite3.h ) if(SQLite3_FOUND AND NOT SQLite3_VERSION) find_file(SQLite3_HEADER "sqlite3.h" HINTS ${SQLite3_INCLUDE_DIRS}) mark_as_advanced(SQLite3_HEADER) if(SQLite3_HEADER) file(STRINGS "${SQLite3_HEADER}" SQLite3_VERSION REGEX "^#define SQLITE_VERSION +\\\"[^\\\"]+\\\"") string(REGEX REPLACE "^#define SQLITE_VERSION +\\\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\\\"$" "\\1.\\2.\\3" SQLite3_VERSION "${SQLite3_VERSION}") endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SQLite3 REQUIRED_VARS SQLite3_LIBRARY VERSION_VAR SQLite3_VERSION)dino-0.4.3/cmake/FindSignalProtocol.cmake0000644000000000000000000000055414452563620017002 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(SignalProtocol PKG_CONFIG_NAME libsignal-protocol-c LIB_NAMES signal-protocol-c INCLUDE_NAMES signal/signal_protocol.h ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SignalProtocol REQUIRED_VARS SignalProtocol_LIBRARY VERSION_VAR SignalProtocol_VERSION) dino-0.4.3/cmake/FindSoup2.cmake0000644000000000000000000000304314452563620015047 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Soup2 PKG_CONFIG_NAME libsoup-2.4 LIB_NAMES soup-2.4 INCLUDE_NAMES libsoup/soup.h INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include DEPENDS GIO ) if(Soup2_FOUND AND NOT Soup2_VERSION) find_file(Soup2_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup_INCLUDE_DIRS}) mark_as_advanced(Soup2_VERSION_HEADER) if(Soup_VERSION_HEADER) file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MAJOR_VERSION "${Soup2_MAJOR_VERSION}") file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MINOR_VERSION "${Soup2_MINOR_VERSION}") file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MICRO_VERSION "${Soup2_MICRO_VERSION}") set(Soup_VERSION "${Soup2_MAJOR_VERSION}.${Soup2_MINOR_VERSION}.${Soup2_MICRO_VERSION}") unset(Soup2_MAJOR_VERSION) unset(Soup2_MINOR_VERSION) unset(Soup2_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Soup2 REQUIRED_VARS Soup2_LIBRARY VERSION_VAR Soup2_VERSION) dino-0.4.3/cmake/FindSoup3.cmake0000644000000000000000000000305114452563620015047 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Soup3 PKG_CONFIG_NAME libsoup-3.0 LIB_NAMES soup-3.0 INCLUDE_NAMES libsoup/soup.h INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include DEPENDS GIO ) if(Soup3_FOUND AND NOT Soup3_VERSION) find_file(Soup3_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup3_INCLUDE_DIRS}) mark_as_advanced(Soup3_VERSION_HEADER) if(Soup3_VERSION_HEADER) file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MAJOR_VERSION "${Soup3_MAJOR_VERSION}") file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MINOR_VERSION "${Soup3_MINOR_VERSION}") file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$") string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MICRO_VERSION "${Soup3_MICRO_VERSION}") set(Soup3_VERSION "${Soup3_MAJOR_VERSION}.${Soup3_MINOR_VERSION}.${Soup3_MICRO_VERSION}") unset(Soup3_MAJOR_VERSION) unset(Soup3_MINOR_VERSION) unset(Soup3_MICRO_VERSION) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Soup3 REQUIRED_VARS Soup3_LIBRARY VERSION_VAR Soup3_VERSION) dino-0.4.3/cmake/FindSrtp2.cmake0000644000000000000000000000052014452563620015046 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(Srtp2 PKG_CONFIG_NAME libsrtp2 LIB_NAMES srtp2 INCLUDE_NAMES srtp2/srtp.h INCLUDE_DIR_SUFFIXES srtp2 srtp2/include ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Srtp2 REQUIRED_VARS Srtp2_LIBRARY VERSION_VAR Srtp2_VERSION)dino-0.4.3/cmake/FindVala.cmake0000644000000000000000000000625214452563620014727 0ustar rootroot## # Find module for the Vala compiler (valac) # # This module determines wheter a Vala compiler is installed on the current # system and where its executable is. # # Call the module using "find_package(Vala) from within your CMakeLists.txt. # # The following variables will be set after an invocation: # # VALA_FOUND Whether the vala compiler has been found or not # VALA_EXECUTABLE Full path to the valac executable if it has been found # VALA_VERSION Version number of the available valac # VALA_USE_FILE Include this file to define the vala_precompile function ## ## # Copyright 2009-2010 Jakob Westhoff. All rights reserved. # Copyright 2010-2011 Daniel Pfeifer # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are those # of the authors and should not be interpreted as representing official policies, # either expressed or implied, of Jakob Westhoff ## # Search for the valac executable in the usual system paths # Some distributions rename the valac to contain the major.minor in the binary name find_package(GObject REQUIRED) find_program(VALA_EXECUTABLE NAMES valac valac-0.38 valac-0.36 valac-0.34 valac-0.32) mark_as_advanced(VALA_EXECUTABLE) # Determine the valac version if(VALA_EXECUTABLE) file(TO_NATIVE_PATH "${VALA_EXECUTABLE}" VALA_EXECUTABLE) execute_process(COMMAND ${VALA_EXECUTABLE} "--version" OUTPUT_VARIABLE VALA_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REPLACE "Vala " "" VALA_VERSION "${VALA_VERSION}") endif(VALA_EXECUTABLE) # Handle the QUIETLY and REQUIRED arguments, which may be given to the find call. # Furthermore set VALA_FOUND to TRUE if Vala has been found (aka. # VALA_EXECUTABLE is set) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Vala FOUND_VAR VALA_FOUND REQUIRED_VARS VALA_EXECUTABLE VERSION_VAR VALA_VERSION) set(VALA_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseVala.cmake") dino-0.4.3/cmake/FindWebRTCAudioProcessing.cmake0000644000000000000000000000077414452563620020154 0ustar rootrootinclude(PkgConfigWithFallback) find_pkg_config_with_fallback(WebRTCAudioProcessing PKG_CONFIG_NAME webrtc-audio-processing LIB_NAMES webrtc_audio_processing INCLUDE_NAMES webrtc/modules/audio_processing/include/audio_processing.h INCLUDE_DIR_SUFFIXES webrtc-audio-processing webrtc_audio_processing ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WebRTCAudioProcessing REQUIRED_VARS WebRTCAudioProcessing_LIBRARY VERSION_VAR WebRTCAudioProcessing_VERSION) dino-0.4.3/cmake/GenerateGXML.cmake0000644000000000000000000001240114452563620015456 0ustar rootrootinclude(CMakeParseArguments) # Generates the resource XML controlling file from resource list (and saves it # to xml_path). It's not recommended to use this function directly, since it # doesn't handle invalid arguments. It is used by the function # COMPILE_GRESOURCES() to create a custom command, so that this function is # invoked at build-time in script mode from CMake. function(GENERATE_GXML xml_path) # Available options: # COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all # registered resources. # STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all # registered resources. # TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all # registered resources. set(GXML_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL STRIPBLANKS_ALL NO_STRIPBLANKS_ALL TOPIXDATA_ALL NO_TOPIXDATA_ALL) # Available one value options: # PREFIX Overrides the resource prefix that is prepended to each # relative file name in registered resources. set(GXML_ONEVALUEARGS PREFIX) # Available multi-value options: # RESOURCES The list of resource files. Whether absolute or relative path is # equal, absolute paths are stripped down to relative ones. If the # absolute path is not inside the given base directory SOURCE_DIR # or CMAKE_SOURCE_DIR (if SOURCE_DIR is not overriden), this # function aborts. set(GXML_MULTIVALUEARGS RESOURCES) # Parse the arguments. cmake_parse_arguments(GXML_ARG "${GXML_OPTIONS}" "${GXML_ONEVALUEARGS}" "${GXML_MULTIVALUEARGS}" "${ARGN}") # Variable to store the double-quote (") string. Since escaping # double-quotes in strings is not possible we need a helper variable that # does this job for us. set(Q \") # Process resources and generate XML file. # Begin with the XML header and header nodes. set(GXML_XML_FILE "") set(GXML_XML_FILE "${GXML_XML_FILE}") # Process each resource. foreach(res ${GXML_ARG_RESOURCES}) if ("${res}" STREQUAL "COMPRESS") set(GXML_COMPRESSION_FLAG ON) elseif ("${res}" STREQUAL "STRIPBLANKS") set(GXML_STRIPBLANKS_FLAG ON) elseif ("${res}" STREQUAL "TOPIXDATA") set(GXML_TOPIXDATA_FLAG ON) else() # The file name. set(GXML_RESOURCE_PATH "${res}") # Append to real resource file dependency list. list(APPEND GXML_RESOURCES_DEPENDENCIES ${GXML_RESOURCE_PATH}) # Assemble node. set(GXML_RES_LINE "${GXML_RESOURCE_PATH}") # Append to file string. set(GXML_XML_FILE "${GXML_XML_FILE}${GXML_RES_LINE}") # Unset variables. unset(GXML_COMPRESSION_FLAG) unset(GXML_STRIPBLANKS_FLAG) unset(GXML_TOPIXDATA_FLAG) endif() endforeach() # Append closing nodes. set(GXML_XML_FILE "${GXML_XML_FILE}") # Use "file" function to generate XML controlling file. get_filename_component(xml_path_only_name "${xml_path}" NAME) file(WRITE ${xml_path} ${GXML_XML_FILE}) endfunction() dino-0.4.3/cmake/GlibCompileResourcesSupport.cmake0000644000000000000000000000062414452563620020716 0ustar rootroot# Path to this file. set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR}) # Finds the glib-compile-resources executable. find_program(GLIB_COMPILE_RESOURCES_EXECUTABLE glib-compile-resources) mark_as_advanced(GLIB_COMPILE_RESOURCES_EXECUTABLE) # Include the cmake files containing the functions. include(${GCR_CMAKE_MACRO_DIR}/CompileGResources.cmake) include(${GCR_CMAKE_MACRO_DIR}/GenerateGXML.cmake) dino-0.4.3/cmake/LargeFileOffsets.c0000644000000000000000000000043714452563620015570 0ustar rootroot#include #define _K ((off_t)1024) #define _M ((off_t)1024 * _K) #define _G ((off_t)1024 * _M) #define _T ((off_t)1024 * _G) int test[(((64 * _G -1) % 671088649) == 268434537) && (((_T - (64 * _G -1) + 255) % 1792151290) == 305159546)? 1: -1]; int main() { return 0; }dino-0.4.3/cmake/MultiFind.cmake0000644000000000000000000000317414452563620015136 0ustar rootrootinclude(CMakeParseArguments) function(find_packages result) cmake_parse_arguments(ARGS "" "" "REQUIRED;OPTIONAL" ${ARGN}) set(_res "") set(_res_libs "") foreach(pkg ${ARGS_REQUIRED}) string(REPLACE ">=" ";" pkg_ ${pkg}) list(GET pkg_ "0" pkg) list(LENGTH pkg_ pkg_has_version) if(pkg_has_version GREATER 1) list(GET pkg_ "1" pkg_version) else() if(${pkg}_GLOBAL_VERSION) set(pkg_version ${${pkg}_GLOBAL_VERSION}) else() unset(pkg_version) endif() endif() find_package(${pkg} ${pkg_version} REQUIRED) list(APPEND _res ${${pkg}_PKG_CONFIG_NAME}) list(APPEND _res_libs ${${pkg}_LIBRARIES}) set(${pkg}_VERSION "${${pkg}_VERSION}" PARENT_SCOPE) endforeach(pkg) foreach(pkg ${ARGS_OPTIONAL}) string(REPLACE ">=" ";" pkg_ ${pkg}) list(GET pkg_ "0" pkg) list(LENGTH pkg_ pkg_has_version) if(pkg_has_version GREATER 1) list(GET pkg_ "1" pkg_version) else() if(${pkg}_GLOBAL_VERSION) set(pkg_version ${${pkg}_GLOBAL_VERSION}) else() unset(pkg_version) endif() endif() find_package(${pkg} ${pkg_version}) if(${pkg}_FOUND) list(APPEND _res ${${pkg}_PKG_CONFIG_NAME}) list(APPEND _res_libs ${${pkg}_LIBRARIES}) set(${pkg}_VERSION "${${pkg}_VERSION}" PARENT_SCOPE) endif() endforeach(pkg) set(${result} "${_res}" PARENT_SCOPE) set(${result}_LIBS "${_res_libs}" PARENT_SCOPE) endfunction() dino-0.4.3/cmake/PkgConfigWithFallback.cmake0000644000000000000000000001162614452563620017367 0ustar rootrootinclude(CMakeParseArguments) function(find_pkg_config_with_fallback name) cmake_parse_arguments(ARGS "" "PKG_CONFIG_NAME" "LIB_NAMES;LIB_DIR_HINTS;INCLUDE_NAMES;INCLUDE_DIR_PATHS;INCLUDE_DIR_HINTS;INCLUDE_DIR_SUFFIXES;DEPENDS" ${ARGN}) set(${name}_PKG_CONFIG_NAME ${ARGS_PKG_CONFIG_NAME} PARENT_SCOPE) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_search_module(${name}_PKG_CONFIG QUIET ${ARGS_PKG_CONFIG_NAME}) endif(PKG_CONFIG_FOUND) if (${name}_PKG_CONFIG_FOUND) # Found via pkg-config, using its result values set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND}) # Try to find real file name of libraries foreach(lib ${${name}_PKG_CONFIG_LIBRARIES}) find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS}) mark_as_advanced(${name}_${lib}_LIBRARY) if(NOT ${name}_${lib}_LIBRARY) unset(${name}_FOUND) endif(NOT ${name}_${lib}_LIBRARY) endforeach(lib) if(${name}_FOUND) set(${name}_LIBRARIES "") foreach(lib ${${name}_PKG_CONFIG_LIBRARIES}) list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY}) endforeach(lib) list(REMOVE_DUPLICATES ${name}_LIBRARIES) set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE) list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY) set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE) set(${name}_INCLUDE_DIRS ${${name}_PKG_CONFIG_INCLUDE_DIRS} PARENT_SCOPE) set(${name}_LIBRARIES ${${name}_PKG_CONFIG_LIBRARIES} PARENT_SCOPE) set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE) set(${name}_VERSION ${${name}_PKG_CONFIG_VERSION} PARENT_SCOPE) if(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED) set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_PKG_CONFIG_CFLAGS_OTHER}") set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_PKG_CONFIG_INCLUDE_DIRS}") set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}") endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) endif(${name}_FOUND) else(${name}_PKG_CONFIG_FOUND) # No success with pkg-config, try via find_library on all lib_names set(${name}_FOUND "1") foreach(lib ${ARGS_LIB_NAMES}) find_library(${name}_${lib}_LIBRARY ${ARGS_LIB_NAMES} HINTS ${ARGS_LIB_DIR_HINTS}) mark_as_advanced(${name}_${lib}_LIBRARY) if(NOT ${name}_${lib}_LIBRARY) unset(${name}_FOUND) endif(NOT ${name}_${lib}_LIBRARY) endforeach(lib) foreach(inc ${ARGS_INCLUDE_NAMES}) find_path(${name}_${inc}_INCLUDE_PATH ${inc} HINTS ${ARGS_INCLUDE_DIR_HINTS} PATHS ${ARGS_INCLUDE_DIR_PATHS} PATH_SUFFIXES ${ARGS_INCLUDE_DIR_SUFFIXES}) mark_as_advanced(${name}_${inc}_INCLUDE_PATH) if(NOT ${name}_${inc}_INCLUDE_PATH) unset(${name}_FOUND) endif(NOT ${name}_${inc}_INCLUDE_PATH) endforeach(inc) if(${name}_FOUND) set(${name}_LIBRARIES "") set(${name}_INCLUDE_DIRS "") foreach(lib ${ARGS_LIB_NAMES}) list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY}) endforeach(lib) foreach(inc ${ARGS_INCLUDE_NAMES}) list(APPEND ${name}_INCLUDE_DIRS ${${name}_${inc}_INCLUDE_PATH}) endforeach(inc) list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY) foreach(dep ${ARGS_DEPENDS}) find_package(${dep} ${${dep}_GLOBAL_VERSION} QUIET) if(${dep}_FOUND) list(APPEND ${name}_INCLUDE_DIRS ${${dep}_INCLUDE_DIRS}) list(APPEND ${name}_LIBRARIES ${${dep}_LIBRARIES}) else(${dep}_FOUND) unset(${name}_FOUND) endif(${dep}_FOUND) endforeach(dep) set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE) set(${name}_INCLUDE_DIRS ${${name}_INCLUDE_DIRS} PARENT_SCOPE) set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE) set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE) unset(${name}_VERSION PARENT_SCOPE) if(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED) set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_INCLUDE_DIRS}") set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}") endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) endif(${name}_FOUND) endif(${name}_PKG_CONFIG_FOUND) endfunction() dino-0.4.3/cmake/PkgConfigWithFallbackOnConfigScript.cmake0000644000000000000000000001262014452563620022172 0ustar rootrootinclude(CMakeParseArguments) function(find_pkg_config_with_fallback_on_config_script name) cmake_parse_arguments(ARGS "" "PKG_CONFIG_NAME" "CONFIG_SCRIPT_NAME" ${ARGN}) set(${name}_PKG_CONFIG_NAME ${ARGS_PKG_CONFIG_NAME} PARENT_SCOPE) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_search_module(${name}_PKG_CONFIG QUIET ${ARGS_PKG_CONFIG_NAME}) endif(PKG_CONFIG_FOUND) if (${name}_PKG_CONFIG_FOUND) # Found via pkg-config, using it's result values set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND}) # Try to find real file name of libraries foreach(lib ${${name}_PKG_CONFIG_LIBRARIES}) find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS}) mark_as_advanced(${name}_${lib}_LIBRARY) if(NOT ${name}_${lib}_LIBRARY) unset(${name}_FOUND) endif(NOT ${name}_${lib}_LIBRARY) endforeach(lib) if(${name}_FOUND) set(${name}_LIBRARIES "") foreach(lib ${${name}_PKG_CONFIG_LIBRARIES}) list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY}) endforeach(lib) list(REMOVE_DUPLICATES ${name}_LIBRARIES) set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE) list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY) set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE) set(${name}_INCLUDE_DIRS ${${name}_PKG_CONFIG_INCLUDE_DIRS} PARENT_SCOPE) set(${name}_LIBRARIES ${${name}_PKG_CONFIG_LIBRARIES} PARENT_SCOPE) set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE) set(${name}_VERSION ${${name}_PKG_CONFIG_VERSION} PARENT_SCOPE) if(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED) set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_PKG_CONFIG_CFLAGS_OTHER}") set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_PKG_CONFIG_INCLUDE_DIRS}") set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}") endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME}) endif(${name}_FOUND) else(${name}_PKG_CONFIG_FOUND) # No success with pkg-config, try via custom a *-config script find_program(${name}_CONFIG_EXECUTABLE NAMES ${ARGS_CONFIG_SCRIPT_NAME}-config) mark_as_advanced(${name}_CONFIG_EXECUTABLE) find_program(${name}_SH_EXECUTABLE NAMES sh) mark_as_advanced(${name}_SH_EXECUTABLE) if(${name}_CONFIG_EXECUTABLE) macro(config_script_fail errcode) if(${errcode}) message(FATAL_ERROR "Error invoking ${ARGS_CONFIG_SCRIPT_NAME}-config: ${errcode}") endif(${errcode}) endmacro(config_script_fail) file(TO_NATIVE_PATH "${${name}_CONFIG_EXECUTABLE}" ${name}_CONFIG_EXECUTABLE) file(TO_NATIVE_PATH "${${name}_SH_EXECUTABLE}" ${name}_SH_EXECUTABLE) execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --version OUTPUT_VARIABLE ${name}_VERSION RESULT_VARIABLE ERRCODE OUTPUT_STRIP_TRAILING_WHITESPACE) config_script_fail(${ERRCODE}) execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --api-version OUTPUT_VARIABLE ${name}_API_VERSION RESULT_VARIABLE ERRCODE OUTPUT_STRIP_TRAILING_WHITESPACE) config_script_fail(${ERRCODE}) execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --cflags OUTPUT_VARIABLE ${name}_CFLAGS RESULT_VARIABLE ERRCODE OUTPUT_STRIP_TRAILING_WHITESPACE) config_script_fail(${ERRCODE}) execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --libs OUTPUT_VARIABLE ${name}_LDFLAGS RESULT_VARIABLE ERRCODE OUTPUT_STRIP_TRAILING_WHITESPACE) config_script_fail(${ERRCODE}) string(TOLOWER ${name} "${name}_LOWER") string(REGEX REPLACE "^(.* |)-l([^ ]*${${name}_LOWER}[^ ]*)( .*|)$" "\\2" ${name}_LIBRARY_NAME "${${name}_LDFLAGS}") string(REGEX REPLACE "^(.* |)-L([^ ]*)( .*|)$" "\\2" ${name}_LIBRARY_DIRS "${${name}_LDFLAGS}") find_library(${name}_LIBRARY ${${name}_LIBRARY_NAME} HINTS ${${name}_LIBRARY_DIRS}) mark_as_advanced(${name}_LIBRARY) set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE) set(${name}_VERSION ${${name}_VERSION} PARENT_SCOPE) unset(${name}_LIBRARY_NAME) unset(${name}_LIBRARY_DIRS) if(NOT TARGET ${name}_LOWER) add_library(${name}_LOWER INTERFACE IMPORTED) set_property(TARGET ${name}_LOWER PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LDFLAGS}") set_property(TARGET ${name}_LOWER PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_CFLAGS}") endif(NOT TARGET ${name}_LOWER) endif(${name}_CONFIG_EXECUTABLE) endif(${name}_PKG_CONFIG_FOUND) endfunction() dino-0.4.3/cmake/SoupVersion.cmake0000644000000000000000000000225014452563620015531 0ustar rootrootfind_package(Nice QUIET) if (Nice_FOUND AND NOT SOUP_VERSION AND NOT USE_SOUP3) file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR Nice_DEPENDENCIES UNRESOLVED_DEPENDENCIES_VAR Nice_UNRESOLVED_DEPENDENCIES LIBRARIES ${Nice_LIBRARY} PRE_INCLUDE_REGEXES "soup|gupnp" PRE_EXCLUDE_REGEXES "." ) foreach (lib ${Nice_DEPENDENCIES}) if (lib MATCHES ".*/libsoup-3.*") set(SOUP_VERSION 3) endif () endforeach () foreach (lib ${Nice_DEPENDENCIES}) if (lib MATCHES ".*/libsoup-2.*") set(SOUP_VERSION 2) endif () endforeach () set(SOUP_VERSION ${SOUP_VERSION} CACHE STRING "Version of libsoup to use") set_property(CACHE SOUP_VERSION PROPERTY STRINGS "2" "3") message(STATUS "Using Soup${SOUP_VERSION} to provide Soup") elseif (NOT SOUP_VERSION) find_package(Soup2 QUIET) find_package(Soup3 QUIET) # Only use libsoup 3 if specifically requested or when libsoup 2 is not available if (Soup3_FOUND AND NOT Soup2_FOUND OR USE_SOUP3) set(SOUP_VERSION 3) else () set(SOUP_VERSION 2) endif () endif () set(Soup "Soup${SOUP_VERSION}")dino-0.4.3/cmake/UseGettext.cmake0000644000000000000000000000237414452563620015345 0ustar rootrootfunction(_gettext_mkdir_for_file file) get_filename_component(dir "${file}" DIRECTORY) file(MAKE_DIRECTORY "${dir}") endfunction() function(gettext_compile project_name) cmake_parse_arguments(ARGS "" "MO_FILES_NAME;TARGET_NAME;SOURCE_DIR;PROJECT_NAME" "" ${ARGN}) if(NOT ARGS_SOURCE_DIR) set(ARGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) endif(NOT ARGS_SOURCE_DIR) file(STRINGS "${ARGS_SOURCE_DIR}/LINGUAS" LINGUAS) set(target_files) foreach(lang ${LINGUAS}) set(source_file ${ARGS_SOURCE_DIR}/${lang}.po) set(target_file ${CMAKE_BINARY_DIR}/locale/${lang}/LC_MESSAGES/${project_name}.mo) _gettext_mkdir_for_file(${target_file}) list(APPEND target_files ${target_file}) add_custom_command(OUTPUT ${target_file} COMMAND ${MSGFMT_EXECUTABLE} --check-format -o ${target_file} ${source_file} DEPENDS ${source_file}) install(FILES ${target_file} DESTINATION ${LOCALE_INSTALL_DIR}/${lang}/LC_MESSAGES) endforeach(lang) if(ARGS_MO_FILES_NAME) set(${ARGS_MO_FILES_NAME} ${target_files} PARENT_SCOPE) endif(ARGS_MO_FILES_NAME) if(ARGS_TARGET_NAME) add_custom_target(${ARGS_TARGET_NAME} DEPENDS ${target_files}) endif(ARGS_TARGET_NAME) endfunction(gettext_compile) dino-0.4.3/cmake/UseVala.cmake0000644000000000000000000003147714452563620014612 0ustar rootroot## # Compile vala files to their c equivalents for further processing. # # The "vala_precompile" function takes care of calling the valac executable on # the given source to produce c files which can then be processed further using # default cmake functions. # # The first parameter provided is a variable, which will be filled with a list # of c files outputted by the vala compiler. This list can than be used in # conjuction with functions like "add_executable" or others to create the # neccessary compile rules with CMake. # # The following sections may be specified afterwards to provide certain options # to the vala compiler: # # SOURCES # A list of .vala files to be compiled. Please take care to add every vala # file belonging to the currently compiled project or library as Vala will # otherwise not be able to resolve all dependencies. # # PACKAGES # A list of vala packages/libraries to be used during the compile cycle. The # package names are exactly the same, as they would be passed to the valac # "--pkg=" option. # # OPTIONS # A list of optional options to be passed to the valac executable. This can be # used to pass "--thread" for example to enable multi-threading support. # # DEFINITIONS # A list of symbols to be used for conditional compilation. They are the same # as they would be passed using the valac "--define=" option. # # CUSTOM_VAPIS # A list of custom vapi files to be included for compilation. This can be # useful to include freshly created vala libraries without having to install # them in the system. # # GENERATE_VAPI # Pass all the needed flags to the compiler to create a vapi for # the compiled library. The provided name will be used for this and a # .vapi file will be created. # # GENERATE_HEADER # Let the compiler generate a header file for the compiled code. There will # be a header file as well as an internal header file being generated called # .h and _internal.h # # The following call is a simple example to the vala_precompile macro showing # an example to every of the optional sections: # # find_package(Vala "0.12" REQUIRED) # include(${VALA_USE_FILE}) # # vala_precompile(VALA_C # SOURCES # source1.vala # source2.vala # source3.vala # PACKAGES # gtk+-2.0 # gio-1.0 # posix # DIRECTORY # gen # OPTIONS # --thread # CUSTOM_VAPIS # some_vapi.vapi # GENERATE_VAPI # myvapi # GENERATE_HEADER # myheader # ) # # Most important is the variable VALA_C which will contain all the generated c # file names after the call. ## ## # Copyright 2009-2010 Jakob Westhoff. All rights reserved. # Copyright 2010-2011 Daniel Pfeifer # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # The views and conclusions contained in the software and documentation are those # of the authors and should not be interpreted as representing official policies, # either expressed or implied, of Jakob Westhoff ## include(CMakeParseArguments) function(_vala_mkdir_for_file file) get_filename_component(dir "${file}" DIRECTORY) file(MAKE_DIRECTORY "${dir}") endfunction() function(vala_precompile output) cmake_parse_arguments(ARGS "FAST_VAPI" "DIRECTORY;GENERATE_HEADER;GENERATE_VAPI;EXPORTS_DIR" "SOURCES;PACKAGES;OPTIONS;DEFINITIONS;CUSTOM_VAPIS;CUSTOM_DEPS;GRESOURCES" ${ARGN}) # Header and internal header is needed to generate internal vapi if (ARGS_GENERATE_VAPI AND NOT ARGS_GENERATE_HEADER) set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI}) endif(ARGS_GENERATE_VAPI AND NOT ARGS_GENERATE_HEADER) if("Ninja" STREQUAL ${CMAKE_GENERATOR} AND NOT DISABLE_FAST_VAPI AND NOT ARGS_GENERATE_HEADER) set(ARGS_FAST_VAPI true) endif() if(ARGS_DIRECTORY) get_filename_component(DIRECTORY ${ARGS_DIRECTORY} ABSOLUTE) else(ARGS_DIRECTORY) set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif(ARGS_DIRECTORY) if(ARGS_EXPORTS_DIR) set(ARGS_EXPORTS_DIR ${CMAKE_BINARY_DIR}/${ARGS_EXPORTS_DIR}) else(ARGS_EXPORTS_DIR) set(ARGS_EXPORTS_DIR ${CMAKE_BINARY_DIR}/exports) endif(ARGS_EXPORTS_DIR) file(MAKE_DIRECTORY "${ARGS_EXPORTS_DIR}") include_directories(${DIRECTORY} ${ARGS_EXPORTS_DIR}) set(vala_pkg_opts "") foreach(pkg ${ARGS_PACKAGES}) list(APPEND vala_pkg_opts "--pkg=${pkg}") endforeach(pkg ${ARGS_PACKAGES}) set(vala_define_opts "") foreach(def ${ARGS_DEFINITIONS}) list(APPEND vala_define_opts "--define=${def}") endforeach(def ${ARGS_DEFINITIONS}) set(custom_vapi_arguments "") if(ARGS_CUSTOM_VAPIS) foreach(vapi ${ARGS_CUSTOM_VAPIS}) if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) list(APPEND custom_vapi_arguments ${vapi}) else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi}) endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) endforeach(vapi ${ARGS_CUSTOM_VAPIS}) endif(ARGS_CUSTOM_VAPIS) set(gresources_args "") if(ARGS_GRESOURCES) set(gresources_args --gresources "${ARGS_GRESOURCES}") endif(ARGS_GRESOURCES) set(in_files "") set(fast_vapi_files "") set(out_files "") set(out_extra_files "") set(out_deps_files "") set(vapi_arguments "") if(ARGS_GENERATE_VAPI) list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.vapi") list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}_internal.vapi") set(vapi_arguments "--vapi=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.vapi" "--internal-vapi=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}_internal.vapi") if(ARGS_PACKAGES) string(REPLACE ";" "\\n" pkgs "${ARGS_PACKAGES};${ARGS_CUSTOM_DEPS}") add_custom_command(OUTPUT "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.deps" COMMAND echo -e "\"${pkgs}\"" > "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.deps" COMMENT "Generating ${ARGS_GENERATE_VAPI}.deps") endif(ARGS_PACKAGES) endif(ARGS_GENERATE_VAPI) set(header_arguments "") if(ARGS_GENERATE_HEADER) list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}.h") list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}_internal.h") list(APPEND header_arguments "--header=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}.h") list(APPEND header_arguments "--internal-header=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}_internal.h") endif(ARGS_GENERATE_HEADER) string(REPLACE " " ";" VALAC_FLAGS ${CMAKE_VALA_FLAGS}) if (VALA_VERSION VERSION_GREATER "0.38") set(VALAC_COLORS "--color=always") endif () if(ARGS_FAST_VAPI) foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}") list(APPEND in_files "${in_file}") string(REPLACE ".vala" ".c" src ${src}) string(REPLACE ".gs" ".c" src ${src}) string(REPLACE ".c" ".vapi" fast_vapi ${src}) set(fast_vapi_file "${DIRECTORY}/${fast_vapi}") list(APPEND fast_vapi_files "${fast_vapi_file}") list(APPEND out_files "${DIRECTORY}/${src}") _vala_mkdir_for_file("${fast_vapi_file}") add_custom_command(OUTPUT ${fast_vapi_file} COMMAND ${VALA_EXECUTABLE} ARGS ${VALAC_COLORS} --fast-vapi ${fast_vapi_file} ${vala_define_opts} ${ARGS_OPTIONS} ${VALAC_FLAGS} ${in_file} DEPENDS ${in_file} COMMENT "Generating fast VAPI ${fast_vapi}" ) endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}") string(REPLACE ".vala" ".c" c_code ${src}) string(REPLACE ".gs" ".c" c_code ${c_code}) string(REPLACE ".c" ".vapi" fast_vapi ${c_code}) set(my_fast_vapi_file "${DIRECTORY}/${fast_vapi}") set(c_code_file "${DIRECTORY}/${c_code}") set(fast_vapi_flags "") set(fast_vapi_stamp "") foreach(fast_vapi_file ${fast_vapi_files}) if(NOT "${fast_vapi_file}" STREQUAL "${my_fast_vapi_file}") list(APPEND fast_vapi_flags --use-fast-vapi "${fast_vapi_file}") list(APPEND fast_vapi_stamp "${fast_vapi_file}") endif() endforeach(fast_vapi_file) _vala_mkdir_for_file("${fast_vapi_file}") get_filename_component(dir "${c_code_file}" DIRECTORY) add_custom_command(OUTPUT ${c_code_file} COMMAND ${VALA_EXECUTABLE} ARGS ${VALAC_COLORS} "-C" "-d" ${dir} ${vala_pkg_opts} ${vala_define_opts} ${gresources_args} ${ARGS_OPTIONS} ${VALAC_FLAGS} ${fast_vapi_flags} ${in_file} ${custom_vapi_arguments} DEPENDS ${fast_vapi_stamp} ${in_file} ${ARGS_CUSTOM_VAPIS} ${ARGS_GRESOURCES} COMMENT "Generating C source ${c_code}" ) endforeach(src) if(NOT "${out_extra_files}" STREQUAL "") add_custom_command(OUTPUT ${out_extra_files} COMMAND ${VALA_EXECUTABLE} ARGS ${VALAC_COLORS} -C -q --disable-warnings ${header_arguments} ${vapi_arguments} "-b" ${CMAKE_CURRENT_SOURCE_DIR} "-d" ${DIRECTORY} ${vala_pkg_opts} ${vala_define_opts} ${gresources_args} ${ARGS_OPTIONS} ${VALAC_FLAGS} ${in_files} ${custom_vapi_arguments} DEPENDS ${in_files} ${ARGS_CUSTOM_VAPIS} ${ARGS_GRESOURCES} COMMENT "Generating VAPI and headers for target ${output}" ) endif() else(ARGS_FAST_VAPI) foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}") list(APPEND in_files "${in_file}") string(REPLACE ".vala" ".c" src ${src}) string(REPLACE ".gs" ".c" src ${src}) list(APPEND out_files "${DIRECTORY}/${src}") _vala_mkdir_for_file("${fast_vapi_file}") endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS}) add_custom_command(OUTPUT ${out_files} ${out_extra_files} COMMAND ${VALA_EXECUTABLE} ARGS ${VALAC_COLORS} -C ${header_arguments} ${vapi_arguments} "-b" ${CMAKE_CURRENT_SOURCE_DIR} "-d" ${DIRECTORY} ${vala_pkg_opts} ${vala_define_opts} ${gresources_args} ${ARGS_OPTIONS} ${VALAC_FLAGS} ${in_files} ${custom_vapi_arguments} DEPENDS ${in_files} ${ARGS_CUSTOM_VAPIS} ${ARGS_GRESOURCES} COMMENT "Generating C code for target ${output}" ) endif(ARGS_FAST_VAPI) set(${output} ${out_files} PARENT_SCOPE) endfunction(vala_precompile) dino-0.4.3/cmake/cmake_uninstall.cmake.in0000644000000000000000000000201414452563620017011 0ustar rootrootif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling: $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) dino-0.4.3/configure0000755000000000000000000002425614452563620013074 0ustar rootroot#!/bin/sh OPTS=`getopt -o "h" --long \ help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,with-libsoup3,\ enable-plugin:,disable-plugin:,\ prefix:,program-prefix:,exec-prefix:,lib-suffix:,\ bindir:,libdir:,includedir:,datadir:,\ host:,build:,\ sbindir:,sysconfdir:,libexecdir:,localstatedir:,sharedstatedir:,mandir:,infodir:,\ -n './configure' -- "$@"` if [ $? != 0 ] ; then echo "-- Ignoring unrecognized options." >&2 ; fi eval set -- "$OPTS" PREFIX=${PREFIX:-/usr/local} ENABLED_PLUGINS= DISABLED_PLUGINS= BUILD_LIBSIGNAL_IN_TREE= BUILD_TESTS= BUILD_TYPE=Debug DISABLE_FAST_VAPI= LIB_SUFFIX= NO_DEBUG= FETCH_ONLY= USE_SOUP3= EXEC_PREFIX= BINDIR= SBINDIR=n SYSCONFDIR= DATADIR= INCLUDEDIR= LIBDIR= LIBEXECDIR= LOCALSTATEDIR= SHAREDSTATEDIR= MANDIR= INFODIR= help() { cat << EOF Usage: ./configure [OPTION]... Defaults for the options (based on current environment) are specified in brackets. Configuration: -h, --help Print this help and exit --disable-fast-vapi Disable the usage of Vala compilers fast-vapi feature. fast-vapi mode is slower when doing clean builds, but faster when doing incremental builds (during development). --fetch-only Only fetch the files required to run ./configure without network access later and exit. --no-debug Build without debug symbols --release Configure to build an optimized release version --with-libsignal-in-tree Build libsignal-protocol-c in tree and link it statically. --with-libsoup3 Build with libsoup-3.0 --with-tests Also build tests. Plugin configuration: --enable-plugin=PLUGIN Enable compilation of plugin PLUGIN. --disable-plugin=PLUGIN Disable compilation of plugin PLUGIN. Installation directories: --prefix=PREFIX Install architecture-independent files in PREFIX [$PREFIX] --program-prefix=PREFIX Same as --prefix --exec-prefix= Install architecture-dependent files in EPREFIX [PREFIX] --lib-suffix=SUFFIX Append SUFFIX to the directory name for libraries By default, \`make install' will install all the files in \`/usr/local/bin', \`/usr/local/lib' etc. You can specify an installation prefix other than \`/usr/local' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --datadir=DIR read-only data [PREFIX/share] For compatibility with autotools, these options will be silently ignored: --host, --build, --sbindir, --sysconfdir, --libexecdir, --sharedstatedir, --localstatedir, --mandir, --infodir Some influential environment variables: CC C compiler command CFLAGS C compiler flags PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path USE_CCACHE decide to use ccache when compiling C objects VALAC Vala compiler command VALACFLAGS Vala compiler flags Use these variables to override the choices made by \`configure' or to help it to find libraries and programs with nonstandard names/locations. EOF } while true; do case "$1" in --prefix ) PREFIX="$2"; shift; shift ;; --enable-plugin ) if [ -z "$ENABLED_PLUGINS" ]; then ENABLED_PLUGINS="$2"; else ENABLED_PLUGINS="$ENABLED_PLUGINS;$2"; fi; shift; shift ;; --disable-plugin ) if [ -z "$DISABLED_PLUGINS" ]; then DISABLED_PLUGINS="$2"; else DISABLED_PLUGINS="$DISABLED_PLUGINS;$2"; fi; shift; shift ;; --valac ) VALA_EXECUTABLE="$2"; shift; shift ;; --valac-flags ) VALAC_FLAGS="$2"; shift; shift ;; --lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;; --with-libsignal-in-tree ) BUILD_LIBSIGNAL_IN_TREE=yes; shift ;; --with-libsoup3 ) USE_SOUP3=yes; shift ;; --disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;; --no-debug ) NO_DEBUG=yes; shift ;; --fetch-only ) FETCH_ONLY=yes; shift ;; --release ) BUILD_TYPE=RelWithDebInfo; shift ;; --with-tests ) BUILD_TESTS=yes; shift ;; # Autotools paths --program-prefix ) PREFIX="$2"; shift; shift ;; --exec-prefix ) EXEC_PREFIX="$2"; shift; shift ;; --bindir ) BINDIR="$2"; shift; shift ;; --datadir ) DATADIR="$2"; shift; shift ;; --includedir ) INCLUDEDIR="$2"; shift; shift ;; --libdir ) LIBDIR="$2"; shift; shift ;; # Autotools paths not used --sbindir ) SBINDIR="$2"; shift; shift ;; --sysconfdir ) SYSCONFDIR="$2"; shift; shift ;; --libexecdir ) LIBEXECDIR="$2"; shift; shift ;; --localstatedir ) LOCALSTATEDIR="$2"; shift; shift ;; --sharedstatedir ) SHAREDSTATEDIR="$2"; shift; shift ;; --mandir ) MANDIR="$2"; shift; shift ;; --infodir ) INFODIR="$2"; shift; shift ;; --host | --build ) shift; shift ;; -h | --help ) help; exit 0 ;; -- ) shift; break ;; * ) break ;; esac done if [ "$BUILD_LIBSIGNAL_IN_TREE" = "yes" ] || [ "$FETCH_ONLY" = "yes" ]; then if [ -d ".git" ]; then git submodule update --init 2>/dev/null else tmp=0 for i in $(cat .gitmodules | grep -n submodule | awk -F ':' '{print $1}') $(wc -l .gitmodules | awk '{print $1}'); do if ! [ $tmp -eq 0 ]; then name=$(cat .gitmodules | head -n $tmp | tail -n 1 | awk -F '"' '{print $2}') def=$(cat .gitmodules | head -n $i | tail -n $(expr "$i" - "$tmp") | awk -F ' ' '{print $1 $2 $3}') path=$(echo "$def" | grep '^path=' | awk -F '=' '{print $2}') url=$(echo "$def" | grep '^url=' | awk -F '=' '{print $2}') branch=$(echo "$def" | grep '^branch=' | awk -F '=' '{print $2}') if ! ls "$path"/* >/dev/null 2>/dev/null; then git=$(which git) if ! [ $? -eq 0 ] || ! [ -x $git ]; then echo "Failed retrieving missing files" exit 5 fi res=$(git clone "$url" "$path" 2>&1) if ! [ $? -eq 0 ] || ! [ -d $path ]; then echo "Failed retrieving missing files: $res" exit 5 fi if [ -n "$branch" ]; then olddir="$(pwd)" cd "$path" res=$(git checkout "$branch" 2>&1) if ! [ $? -eq 0 ]; then echo "Failed retrieving missing files: $res" exit 5 fi cd "$olddir" fi echo "Submodule path '$path': checked out '$branch' (via git clone)" fi fi tmp=$i done fi fi if [ "$FETCH_ONLY" = "yes" ]; then exit 0; fi if [ ! -x "$(which cmake 2>/dev/null)" ] then echo "-!- CMake required." exit 1 fi ninja_bin="$(which ninja-build 2>/dev/null)" if ! [ -x "$ninja_bin" ]; then ninja_bin="$(which ninja 2>/dev/null)" fi if [ -x "$ninja_bin" ]; then ninja_version=`$ninja_bin --version 2>/dev/null` if [ $? -eq 0 ]; then if [ -d build ]; then last_ninja_version=`cat build/.ninja_version 2>/dev/null` else last_ninja_version=0 fi if [ "$ninja_version" != "$last_ninja_version" ]; then echo "-- Found Ninja: $ninja_bin (found version \"$ninja_version\")" fi cmake_type="Ninja" exec_bin="$ninja_bin" exec_command="$exec_bin" elif [ "/usr/sbin/ninja" = "$ninja_bin" ]; then echo "-- Ninja at $ninja_bin is not usable. Did you install 'ninja' instead of 'ninja-build'?" fi fi if ! [ -x "$exec_bin" ]; then make_bin="$(which make 2>/dev/null)" if [ -x "$make_bin" ]; then echo "-- Found Make: $make_bin" cmake_type="Unix Makefiles" exec_bin="$make_bin" exec_command="$exec_bin" echo "-- Running with make. Using Ninja (ninja-build) might improve build experience." fi fi if ! [ -x "$exec_bin" ]; then echo "-!- No compatible build system (Ninja, Make) found." exit 4 fi if [ -f ./build ]; then echo "-!- ./build file exists. ./configure can't continue" exit 2 fi if [ -d build ]; then last_type=`cat build/.cmake_type` if [ "$cmake_type" != "$last_type" ] then echo "-- Using different build system, cleaning build system files" cd build rm -r CMakeCache.txt CMakeFiles cd .. fi fi mkdir -p build cd build echo "$cmake_type" > .cmake_type echo "$ninja_version" > .ninja_version cmake -G "$cmake_type" \ -DCMAKE_INSTALL_PREFIX="$PREFIX" \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DENABLED_PLUGINS="$ENABLED_PLUGINS" \ -DDISABLED_PLUGINS="$DISABLED_PLUGINS" \ -DBUILD_TESTS="$BUILD_TESTS" \ -DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \ -DUSE_SOUP3="$USE_SOUP3" \ -DVALA_EXECUTABLE="$VALAC" \ -DCMAKE_VALA_FLAGS="$VALACFLAGS" \ -DDISABLE_FAST_VAPI="$DISABLE_FAST_VAPI" \ -DLIB_SUFFIX="$LIB_SUFFIX" \ -DNO_DEBUG="$NO_DEBUG" \ -DEXEC_INSTALL_PREFIX="$EXEC_PREFIX" \ -DSHARE_INSTALL_PREFIX="$DATADIR" \ -DBIN_INSTALL_DIR="$BINDIR" \ -DINCLUDE_INSTALL_DIR="$INCLUDEDIR" \ -DLIB_INSTALL_DIR="$LIBDIR" \ -Wno-dev \ .. || exit 9 if [ "$cmake_type" = "Ninja" ]; then cat << EOF > Makefile default: @sh -c "$exec_command" %: @sh -c "$exec_command \"\$@\"" EOF fi cd .. cat << EOF > Makefile default: @sh -c "cd build; $exec_command" distclean: clean uninstall test: default echo "make test not yet supported" %: @sh -c "cd build; $exec_command \"\$@\"" EOF echo "-- Configured. Type 'make' to build, 'make install' to install." dino-0.4.3/crypto-vala/0000755000000000000000000000000014452563620013415 5ustar rootrootdino-0.4.3/crypto-vala/CMakeLists.txt0000644000000000000000000000232014452563620016152 0ustar rootrootfind_package(GCrypt REQUIRED) find_package(Srtp2 REQUIRED) find_packages(CRYPTO_VALA_PACKAGES REQUIRED GLib GObject GIO ) vala_precompile(CRYPTO_VALA_C SOURCES "src/cipher.vala" "src/cipher_converter.vala" "src/error.vala" "src/random.vala" "src/srtp.vala" CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gcrypt.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/vapi/libsrtp2.vapi" PACKAGES ${CRYPTO_VALA_PACKAGES} GENERATE_VAPI crypto-vala GENERATE_HEADER crypto-vala ) add_custom_target(crypto-vala-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/crypto-vala.deps ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="crypto-vala") add_library(crypto-vala SHARED ${CRYPTO_VALA_C}) add_dependencies(crypto-vala crypto-vala-vapi) target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt libsrtp2) set_target_properties(crypto-vala PROPERTIES VERSION 0.0 SOVERSION 0) install(TARGETS crypto-vala ${TARGET_INSTALL}) install(FILES ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/crypto-vala.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/crypto-vala.h DESTINATION ${INCLUDE_INSTALL_DIR}) dino-0.4.3/crypto-vala/src/0000755000000000000000000000000014452563620014204 5ustar rootrootdino-0.4.3/crypto-vala/src/cipher.vala0000644000000000000000000001364714452563620016336 0ustar rootrootnamespace Crypto { public class SymmetricCipher { private GCrypt.Cipher.Cipher cipher; public static bool supports(string algo_name) { GCrypt.Cipher.Algorithm algo; GCrypt.Cipher.Mode mode; GCrypt.Cipher.Flag flags; return parse(algo_name, out algo, out mode, out flags); } private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) { switch (mode) { case GCrypt.Cipher.Mode.ECB: return "ECB"; case GCrypt.Cipher.Mode.CFB: return "CFB"; case GCrypt.Cipher.Mode.CBC: return "CBC"; case GCrypt.Cipher.Mode.STREAM: return "STREAM"; case GCrypt.Cipher.Mode.OFB: return "OFB"; case GCrypt.Cipher.Mode.CTR: return "CTR"; case GCrypt.Cipher.Mode.AESWRAP: return "AESWRAP"; case GCrypt.Cipher.Mode.GCM: return "GCM"; case GCrypt.Cipher.Mode.POLY1305: return "POLY1305"; case GCrypt.Cipher.Mode.OCB: return "OCB"; case GCrypt.Cipher.Mode.CFB8: return "CFB8"; // case GCrypt.Cipher.Mode.XTS: return "XTS"; // Not supported in gcrypt < 1.8 } return "NONE"; } private static GCrypt.Cipher.Mode mode_from_string(string name) { switch (name) { case "ECB": return GCrypt.Cipher.Mode.ECB; case "CFB": return GCrypt.Cipher.Mode.CFB; case "CBC": return GCrypt.Cipher.Mode.CBC; case "STREAM": return GCrypt.Cipher.Mode.STREAM; case "OFB": return GCrypt.Cipher.Mode.OFB; case "CTR": return GCrypt.Cipher.Mode.CTR; case "AESWRAP": return GCrypt.Cipher.Mode.AESWRAP; case "GCM": return GCrypt.Cipher.Mode.GCM; case "POLY1305": return GCrypt.Cipher.Mode.POLY1305; case "OCB": return GCrypt.Cipher.Mode.OCB; case "CFB8": return GCrypt.Cipher.Mode.CFB8; // case "XTS": return GCrypt.Cipher.Mode.XTS; // Not supported in gcrypt < 1.8 } return GCrypt.Cipher.Mode.NONE; } private static string flags_to_string(GCrypt.Cipher.Flag flags) { string? s = null; if ((GCrypt.Cipher.Flag.CBC_MAC & flags) != 0) s = (s == null ? "" : @"$s-") + "MAC"; if ((GCrypt.Cipher.Flag.CBC_CTS & flags) != 0) s = (s == null ? "" : @"$s-") + "CTS"; if ((GCrypt.Cipher.Flag.ENABLE_SYNC & flags) != 0) s = (s == null ? "" : @"$s-") + "SYNC"; if ((GCrypt.Cipher.Flag.SECURE & flags) != 0) s = (s == null ? "" : @"$s-") + "SECURE"; return s ?? "NONE"; } private static GCrypt.Cipher.Flag flag_from_string(string flag_name) { if (flag_name == "SECURE") return GCrypt.Cipher.Flag.SECURE; if (flag_name == "SYNC") return GCrypt.Cipher.Flag.ENABLE_SYNC; if (flag_name == "CTS") return GCrypt.Cipher.Flag.CBC_CTS; if (flag_name == "MAC") return GCrypt.Cipher.Flag.CBC_MAC; return 0; } private static GCrypt.Cipher.Flag flags_from_string(string flag_names) { GCrypt.Cipher.Flag flags = 0; foreach(string flag in flag_names.split("-")) { flags |= flag_from_string(flag); } return flags; } private static bool parse(string algo_name, out GCrypt.Cipher.Algorithm algo, out GCrypt.Cipher.Mode mode, out GCrypt.Cipher.Flag flags) { algo = GCrypt.Cipher.Algorithm.NONE; mode = GCrypt.Cipher.Mode.NONE; flags = 0; string[] algo_parts = algo_name.split("-", 3); algo = GCrypt.Cipher.Algorithm.from_string(algo_parts[0]); if (algo_parts.length >= 2) { mode = mode_from_string(algo_parts[1]); } if (algo_parts.length == 3) { flags |= flags_from_string(algo_parts[2]); } return to_algo_name(algo, mode, flags) == algo_name; } private static string to_algo_name(GCrypt.Cipher.Algorithm algo = GCrypt.Cipher.Algorithm.NONE, GCrypt.Cipher.Mode mode = GCrypt.Cipher.Mode.NONE, GCrypt.Cipher.Flag flags = 0) { if (flags != 0) { return @"$algo-$(mode_to_string(mode))-$(flags_to_string(flags))"; } else if (mode != GCrypt.Cipher.Mode.NONE) { return @"$algo-$(mode_to_string(mode))"; } else { return algo.to_string(); } } public SymmetricCipher(string algo_name) throws Error { GCrypt.Cipher.Algorithm algo; GCrypt.Cipher.Mode mode; GCrypt.Cipher.Flag flags; if (parse(algo_name, out algo, out mode, out flags)) { this.gcrypt(algo, mode, flags); } else { throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); } } private SymmetricCipher.gcrypt(GCrypt.Cipher.Algorithm algo, GCrypt.Cipher.Mode mode, GCrypt.Cipher.Flag flags) throws Error { may_throw_gcrypt_error(GCrypt.Cipher.Cipher.open(out this.cipher, algo, mode, flags)); } public void set_key(uint8[] key) throws Error { may_throw_gcrypt_error(cipher.set_key(key)); } public void set_iv(uint8[] iv) throws Error { may_throw_gcrypt_error(cipher.set_iv(iv)); } public void set_counter_vector(uint8[] ctr) throws Error { may_throw_gcrypt_error(cipher.set_counter_vector(ctr)); } public void reset() throws Error { may_throw_gcrypt_error(cipher.reset()); } public uint8[] get_tag(size_t taglen) throws Error { uint8[] tag = new uint8[taglen]; may_throw_gcrypt_error(cipher.get_tag(tag)); return tag; } public void check_tag(uint8[] tag) throws Error { may_throw_gcrypt_error(cipher.check_tag(tag)); } public void encrypt(uint8[] output, uint8[] input) throws Error { may_throw_gcrypt_error(cipher.encrypt(output, input)); } public void decrypt(uint8[] output, uint8[] input) throws Error { may_throw_gcrypt_error(cipher.decrypt(output, input)); } public void sync() throws Error { may_throw_gcrypt_error(cipher.sync()); } } } dino-0.4.3/crypto-vala/src/cipher_converter.vala0000644000000000000000000001040014452563620020405 0ustar rootrootusing GLib; namespace Crypto { public abstract class SymmetricCipherConverter : Converter, Object { internal SymmetricCipher cipher; internal size_t attached_taglen; public abstract ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError; public uint8[] get_tag(size_t taglen) throws Error { return cipher.get_tag(taglen); } public void check_tag(uint8[] tag) throws Error { cipher.check_tag(tag); } public void reset() { try { cipher.reset(); } catch (Crypto.Error e) { warning(@"$(e.domain) error while resetting cipher: $(e.message)"); } } } public class SymmetricCipherEncrypter : SymmetricCipherConverter { public SymmetricCipherEncrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { this.cipher = (owned) cipher; this.attached_taglen = attached_taglen; } public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } if ((flags & ConverterFlags.INPUT_AT_END) != 0 && inbuf.length + attached_taglen > outbuf.length) { throw new IOError.NO_SPACE("CipherConverter needs additional output space to attach tag"); } try { if (inbuf.length > 0) { cipher.encrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; if ((flags & ConverterFlags.INPUT_AT_END) != 0) { if (attached_taglen > 0) { Memory.copy((uint8*)outbuf + inbuf.length, get_tag(attached_taglen), attached_taglen); bytes_written = inbuf.length + attached_taglen; } return ConverterResult.FINISHED; } if ((flags & ConverterFlags.FLUSH) != 0) { return ConverterResult.FLUSHED; } return ConverterResult.CONVERTED; } catch (Crypto.Error e) { throw new IOError.FAILED(@"$(e.domain) error while decrypting: $(e.message)"); } } } public class SymmetricCipherDecrypter : SymmetricCipherConverter { public SymmetricCipherDecrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { this.cipher = (owned) cipher; this.attached_taglen = attached_taglen; } public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length + attached_taglen) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } if ((flags & ConverterFlags.INPUT_AT_END) != 0 && inbuf.length < attached_taglen) { throw new IOError.PARTIAL_INPUT("CipherConverter needs additional input to read tag"); } else if ((flags & ConverterFlags.INPUT_AT_END) == 0 && inbuf.length < attached_taglen + 1) { throw new IOError.PARTIAL_INPUT("CipherConverter needs additional input to make sure to not accidentally read tag"); } try { inbuf.length -= (int) attached_taglen; if (inbuf.length > 0) { cipher.decrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; inbuf.length += (int) attached_taglen; if ((flags & ConverterFlags.INPUT_AT_END) != 0) { if (attached_taglen > 0) { check_tag(inbuf[(inbuf.length - attached_taglen):inbuf.length]); bytes_read = inbuf.length; } return ConverterResult.FINISHED; } if ((flags & ConverterFlags.FLUSH) != 0) { return ConverterResult.FLUSHED; } return ConverterResult.CONVERTED; } catch (Crypto.Error e) { throw new IOError.FAILED(@"$(e.domain) error while decrypting: $(e.message)"); } } } } dino-0.4.3/crypto-vala/src/error.vala0000644000000000000000000000043014452563620016177 0ustar rootrootnamespace Crypto { public errordomain Error { ILLEGAL_ARGUMENTS, GCRYPT, AUTHENTICATION_FAILED, UNKNOWN } internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error { if (((int)e) != 0) { throw new Crypto.Error.GCRYPT(e.to_string()); } } }dino-0.4.3/crypto-vala/src/random.vala0000644000000000000000000000015214452563620016327 0ustar rootrootnamespace Crypto { public static void randomize(uint8[] buffer) { GCrypt.Random.randomize(buffer); } }dino-0.4.3/crypto-vala/src/srtp.vala0000644000000000000000000001044514452563620016045 0ustar rootrootusing Srtp; namespace Crypto.Srtp { public const string AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80"; public const string AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32"; public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80"; public class Session { public bool has_encrypt { get; private set; default = false; } public bool has_decrypt { get; private set; default = false; } private Context encrypt_context; private Context decrypt_context; static construct { init(); install_log_handler(log); } private static void log(LogLevel level, string msg) { print(@"SRTP[$level]: $msg\n"); } public Session() { Context.create(out encrypt_context, null); Context.create(out decrypt_context, null); } public uint8[] encrypt_rtp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = encrypt_context.protect(buf, ref buf_use); if (res != ErrorStatus.ok) { throw new Error.UNKNOWN(@"SRTP encrypt failed: $res"); } buf.length = buf_use; return buf; } public uint8[] decrypt_rtp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = decrypt_context.unprotect(buf, ref buf_use); switch (res) { case ErrorStatus.auth_fail: throw new Error.AUTHENTICATION_FAILED("SRTP packet failed the message authentication check"); case ErrorStatus.ok: break; default: throw new Error.UNKNOWN(@"SRTP decrypt failed: $res"); } uint8[] ret = new uint8[buf_use]; GLib.Memory.copy(ret, buf, buf_use); return ret; } public uint8[] encrypt_rtcp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN + 4]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = encrypt_context.protect_rtcp(buf, ref buf_use); if (res != ErrorStatus.ok) { throw new Error.UNKNOWN(@"SRTCP encrypt failed: $res"); } buf.length = buf_use; return buf; } public uint8[] decrypt_rtcp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = decrypt_context.unprotect_rtcp(buf, ref buf_use); switch (res) { case ErrorStatus.auth_fail: throw new Error.AUTHENTICATION_FAILED("SRTCP packet failed the message authentication check"); case ErrorStatus.ok: break; default: throw new Error.UNKNOWN(@"SRTCP decrypt failed: $res"); } uint8[] ret = new uint8[buf_use]; GLib.Memory.copy(ret, buf, buf_use); return ret; } private Policy create_policy(string profile) { Policy policy = Policy(); switch (profile) { case AES_CM_128_HMAC_SHA1_80: policy.rtp.set_aes_cm_128_hmac_sha1_80(); policy.rtcp.set_aes_cm_128_hmac_sha1_80(); break; } return policy; } public void set_encryption_key(string profile, uint8[] key, uint8[] salt) { Policy policy = create_policy(profile); policy.ssrc.type = SsrcType.any_outbound; policy.key = new uint8[key.length + salt.length]; Memory.copy(policy.key, key, key.length); Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); policy.next = null; encrypt_context.add_stream(ref policy); has_encrypt = true; } public void set_decryption_key(string profile, uint8[] key, uint8[] salt) { Policy policy = create_policy(profile); policy.ssrc.type = SsrcType.any_inbound; policy.key = new uint8[key.length + salt.length]; Memory.copy(policy.key, key, key.length); Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); policy.next = null; decrypt_context.add_stream(ref policy); has_decrypt = true; } } }dino-0.4.3/crypto-vala/vapi/0000755000000000000000000000000014452563620014354 5ustar rootrootdino-0.4.3/crypto-vala/vapi/gcrypt.vapi0000644000000000000000000004650414452563620016556 0ustar rootroot/* gcrypt.vapi * * Copyright: * 2008 Jiqing Qiang * 2008, 2010, 2012-2013 Evan Nemerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jiqing Qiang * Evan Nemerson */ [CCode (cheader_filename = "gcrypt.h", lower_case_cprefix = "gcry_")] namespace GCrypt { [CCode (cname = "gpg_err_source_t", cprefix = "GPG_ERR_SOURCE_")] public enum ErrorSource { UNKNOWN, GCRYPT, GPG, GPGSM, GPGAGENT, PINENTRY, SCD, GPGME, KEYBOX, KSBA, DIRMNGR, GSTI, ANY, USER_1, USER_2, USER_3, USER_4, /* This is one more than the largest allowed entry. */ DIM } [CCode (cname = "gpg_err_code_t", cprefix = "GPG_ERR_")] public enum ErrorCode { NO_ERROR, GENERAL, UNKNOWN_PACKET, UNKNOWN_VERSION, PUBKEY_ALGO, DIGEST_ALGO, BAD_PUBKEY, BAD_SECKEY, BAD_SIGNATURE, NO_PUBKEY, CHECKSUM, BAD_PASSPHRASE, CIPHER_ALGO, KEYRING_OPEN, INV_PACKET, INV_ARMOR, NO_USER_ID, NO_SECKEY, WRONG_SECKEY, BAD_KEY, COMPR_ALGO, NO_PRIME, NO_ENCODING_METHOD, NO_ENCRYPTION_SCHEME, NO_SIGNATURE_SCHEME, INV_ATTR, NO_VALUE, NOT_FOUND, VALUE_NOT_FOUND, SYNTAX, BAD_MPI, INV_PASSPHRASE, SIG_CLASS, RESOURCE_LIMIT, INV_KEYRING, TRUSTDB, BAD_CERT, INV_USER_ID, UNEXPECTED, TIME_CONFLICT, KEYSERVER, WRONG_PUBKEY_ALGO, TRIBUTE_TO_D_A, WEAK_KEY, INV_KEYLEN, INV_ARG, BAD_URI, INV_URI, NETWORK, UNKNOWN_HOST, SELFTEST_FAILED, NOT_ENCRYPTED, NOT_PROCESSED, UNUSABLE_PUBKEY, UNUSABLE_SECKEY, INV_VALUE, BAD_CERT_CHAIN, MISSING_CERT, NO_DATA, BUG, NOT_SUPPORTED, INV_OP, TIMEOUT, INTERNAL, EOF_GCRYPT, INV_OBJ, TOO_SHORT, TOO_LARGE, NO_OBJ, NOT_IMPLEMENTED, CONFLICT, INV_CIPHER_MODE, INV_FLAG, INV_HANDLE, TRUNCATED, INCOMPLETE_LINE, INV_RESPONSE, NO_AGENT, AGENT, INV_DATA, ASSUAN_SERVER_FAULT, ASSUAN, INV_SESSION_KEY, INV_SEXP, UNSUPPORTED_ALGORITHM, NO_PIN_ENTRY, PIN_ENTRY, BAD_PIN, INV_NAME, BAD_DATA, INV_PARAMETER, WRONG_CARD, NO_DIRMNGR, DIRMNGR, CERT_REVOKED, NO_CRL_KNOWN, CRL_TOO_OLD, LINE_TOO_LONG, NOT_TRUSTED, CANCELED, BAD_CA_CERT, CERT_EXPIRED, CERT_TOO_YOUNG, UNSUPPORTED_CERT, UNKNOWN_SEXP, UNSUPPORTED_PROTECTION, CORRUPTED_PROTECTION, AMBIGUOUS_NAME, CARD, CARD_RESET, CARD_REMOVED, INV_CARD, CARD_NOT_PRESENT, NO_PKCS15_APP, NOT_CONFIRMED, CONFIGURATION, NO_POLICY_MATCH, INV_INDEX, INV_ID, NO_SCDAEMON, SCDAEMON, UNSUPPORTED_PROTOCOL, BAD_PIN_METHOD, CARD_NOT_INITIALIZED, UNSUPPORTED_OPERATION, WRONG_KEY_USAGE, NOTHING_FOUND, WRONG_BLOB_TYPE, MISSING_VALUE, HARDWARE, PIN_BLOCKED, USE_CONDITIONS, PIN_NOT_SYNCED, INV_CRL, BAD_BER, INV_BER, ELEMENT_NOT_FOUND, IDENTIFIER_NOT_FOUND, INV_TAG, INV_LENGTH, INV_KEYINFO, UNEXPECTED_TAG, NOT_DER_ENCODED, NO_CMS_OBJ, INV_CMS_OBJ, UNKNOWN_CMS_OBJ, UNSUPPORTED_CMS_OBJ, UNSUPPORTED_ENCODING, UNSUPPORTED_CMS_VERSION, UNKNOWN_ALGORITHM, INV_ENGINE, PUBKEY_NOT_TRUSTED, DECRYPT_FAILED, KEY_EXPIRED, SIG_EXPIRED, ENCODING_PROBLEM, INV_STATE, DUP_VALUE, MISSING_ACTION, MODULE_NOT_FOUND, INV_OID_STRING, INV_TIME, INV_CRL_OBJ, UNSUPPORTED_CRL_VERSION, INV_CERT_OBJ, UNKNOWN_NAME, LOCALE_PROBLEM, NOT_LOCKED, PROTOCOL_VIOLATION, INV_MAC, INV_REQUEST, UNKNOWN_EXTN, UNKNOWN_CRIT_EXTN, LOCKED, UNKNOWN_OPTION, UNKNOWN_COMMAND, BUFFER_TOO_SHORT, SEXP_INV_LEN_SPEC, SEXP_STRING_TOO_LONG, SEXP_UNMATCHED_PAREN, SEXP_NOT_CANONICAL, SEXP_BAD_CHARACTER, SEXP_BAD_QUOTATION, SEXP_ZERO_PREFIX, SEXP_NESTED_DH, SEXP_UNMATCHED_DH, SEXP_UNEXPECTED_PUNC, SEXP_BAD_HEX_CHAR, SEXP_ODD_HEX_NUMBERS, SEXP_BAD_OCT_CHAR, ASS_GENERAL, ASS_ACCEPT_FAILED, ASS_CONNECT_FAILED, ASS_INV_RESPONSE, ASS_INV_VALUE, ASS_INCOMPLETE_LINE, ASS_LINE_TOO_LONG, ASS_NESTED_COMMANDS, ASS_NO_DATA_CB, ASS_NO_INQUIRE_CB, ASS_NOT_A_SERVER, ASS_NOT_A_CLIENT, ASS_SERVER_START, ASS_READ_ERROR, ASS_WRITE_ERROR, ASS_TOO_MUCH_DATA, ASS_UNEXPECTED_CMD, ASS_UNKNOWN_CMD, ASS_SYNTAX, ASS_CANCELED, ASS_NO_INPUT, ASS_NO_OUTPUT, ASS_PARAMETER, ASS_UNKNOWN_INQUIRE, USER_1, USER_2, USER_3, USER_4, USER_5, USER_6, USER_7, USER_8, USER_9, USER_10, USER_11, USER_12, USER_13, USER_14, USER_15, USER_16, MISSING_ERRNO, UNKNOWN_ERRNO, EOF, E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EAUTH, EBACKGROUND, EBADE, EBADF, EBADFD, EBADMSG, EBADR, EBADRPC, EBADRQC, EBADSLT, EBFONT, EBUSY, ECANCELED, ECHILD, ECHRNG, ECOMM, ECONNABORTED, ECONNREFUSED, ECONNRESET, ED, EDEADLK, EDEADLOCK, EDESTADDRREQ, EDIED, EDOM, EDOTDOT, EDQUOT, EEXIST, EFAULT, EFBIG, EFTYPE, EGRATUITOUS, EGREGIOUS, EHOSTDOWN, EHOSTUNREACH, EIDRM, EIEIO, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, EISNAM, EL2HLT, EL2NSYNC, EL3HLT, EL3RST, ELIBACC, ELIBBAD, ELIBEXEC, ELIBMAX, ELIBSCN, ELNRNG, ELOOP, EMEDIUMTYPE, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, ENAVAIL, ENEEDAUTH, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOANO, ENOBUFS, ENOCSI, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEDIUM, ENOMEM, ENOMSG, ENONET, ENOPKG, ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTBLK, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTNAM, ENOTSOCK, ENOTSUP, ENOTTY, ENOTUNIQ, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPFNOSUPPORT, EPIPE, EPROCLIM, EPROCUNAVAIL, EPROGMISMATCH, EPROGUNAVAIL, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EREMCHG, EREMOTE, EREMOTEIO, ERESTART, EROFS, ERPCMISMATCH, ESHUTDOWN, ESOCKTNOSUPPORT, ESPIPE, ESRCH, ESRMNT, ESTALE, ESTRPIPE, ETIME, ETIMEDOUT, ETOOMANYREFS, ETXTBSY, EUCLEAN, EUNATCH, EUSERS, EWOULDBLOCK, EXDEV, EXFULL, /* This is one more than the largest allowed entry. */ CODE_DIM } [CCode (cname = "gcry_error_t", cprefix = "gpg_err_")] public struct Error : uint { [CCode (cname = "gcry_err_make")] public Error (ErrorSource source, ErrorCode code); [CCode (cname = "gcry_err_make_from_errno")] public Error.from_errno (ErrorSource source, int err); public ErrorCode code (); public ErrorSource source (); [CCode (cname = "gcry_strerror")] public unowned string to_string (); [CCode (cname = "gcry_strsource")] public unowned string source_to_string (); } [CCode (cname = "enum gcry_ctl_cmds", cprefix = "GCRYCTL_")] public enum ControlCommand { SET_KEY, SET_IV, CFB_SYNC, RESET, FINALIZE, GET_KEYLEN, GET_BLKLEN, TEST_ALGO, IS_SECURE, GET_ASNOID, ENABLE_ALGO, DISABLE_ALGO, DUMP_RANDOM_STATS, DUMP_SECMEM_STATS, GET_ALGO_NPKEY, GET_ALGO_NSKEY, GET_ALGO_NSIGN, GET_ALGO_NENCR, SET_VERBOSITY, SET_DEBUG_FLAGS, CLEAR_DEBUG_FLAGS, USE_SECURE_RNDPOOL, DUMP_MEMORY_STATS, INIT_SECMEM, TERM_SECMEM, DISABLE_SECMEM_WARN, SUSPEND_SECMEM_WARN, RESUME_SECMEM_WARN, DROP_PRIVS, ENABLE_M_GUARD, START_DUMP, STOP_DUMP, GET_ALGO_USAGE, IS_ALGO_ENABLED, DISABLE_INTERNAL_LOCKING, DISABLE_SECMEM, INITIALIZATION_FINISHED, INITIALIZATION_FINISHED_P, ANY_INITIALIZATION_P, SET_CBC_CTS, SET_CBC_MAC, SET_CTR, ENABLE_QUICK_RANDOM, SET_RANDOM_SEED_FILE, UPDATE_RANDOM_SEED_FILE, SET_THREAD_CBS, FAST_POLL } public Error control (ControlCommand cmd, ...); [CCode (lower_case_cname = "cipher_")] namespace Cipher { [CCode (cname = "enum gcry_cipher_algos", cprefix = "GCRY_CIPHER_")] public enum Algorithm { NONE, IDEA, 3DES, CAST5, BLOWFISH, SAFER_SK128, DES_SK, AES, AES128, RIJNDAEL, RIJNDAEL128, AES192, RIJNDAEL192, AES256, RIJNDAEL256, TWOFISH, TWOFISH128, ARCFOUR, DES, SERPENT128, SERPENT192, SERPENT256, RFC2268_40, RFC2268_128, SEED, CAMELLIA128, CAMELLIA192, CAMELLIA256, SALSA20, SALSA20R12, GOST28147, CHACHA20; [CCode (cname = "gcry_cipher_algo_info")] public Error info (ControlCommand what, ref uchar[] buffer); [CCode (cname = "gcry_cipher_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_cipher_map_name")] public static Algorithm from_string (string name); [CCode (cname = "gcry_cipher_map_oid")] public static Algorithm from_oid (string oid); } [CCode (cname = "enum gcry_cipher_modes", cprefix = "GCRY_CIPHER_MODE_")] public enum Mode { NONE, /* No mode specified */ ECB, /* Electronic Codebook */ CFB, /* Cipher Feedback */ CBC, /* Cipher Block Chaining */ STREAM, /* Used with stream ciphers */ OFB, /* Output Feedback */ CTR, /* Counter */ AESWRAP, /* AES-WRAP algorithm */ CCM, /* Counter with CBC-MAC */ GCM, /* Galois/Counter Mode */ POLY1305, /* Poly1305 based AEAD mode */ OCB, /* OCB3 mode */ CFB8, /* Cipher Feedback /* Poly1305 based AEAD mode. */ XTS; /* XTS mode */ public unowned string to_string () { switch (this) { case ECB: return "ECB"; case CFB: return "CFB"; case CBC: return "CBC"; case STREAM: return "STREAM"; case OFB: return "OFB"; case CTR: return "CTR"; case AESWRAP: return "AESWRAP"; case GCM: return "GCM"; case POLY1305: return "POLY1305"; case OCB: return "OCB"; case CFB8: return "CFB8"; case XTS: return "XTS"; } return "NONE"; } public static Mode from_string (string name) { switch (name) { case "ECB": return ECB; case "CFB": return CFB; case "CBC": return CBC; case "STREAM": return STREAM; case "OFB": return OFB; case "CTR": return CTR; case "AESWRAP": return AESWRAP; case "GCM": return GCM; case "POLY1305": return POLY1305; case "OCB": return OCB; case "CFB8": return CFB8; case "XTS": return XTS; } return NONE; } } [CCode (cname = "enum gcry_cipher_flags", cprefix = "GCRY_CIPHER_")] public enum Flag { SECURE, /* Allocate in secure memory. */ ENABLE_SYNC, /* Enable CFB sync mode. */ CBC_CTS, /* Enable CBC cipher text stealing (CTS). */ CBC_MAC /* Enable CBC message auth. code (MAC). */ } [CCode (cname = "gcry_cipher_hd_t", lower_case_cprefix = "gcry_cipher_", free_function = "gcry_cipher_close")] [SimpleType] public struct Cipher { public static Error open (out Cipher cipher, Algorithm algo, Mode mode, Flag flags); public void close (); [CCode (cname = "gcry_cipher_ctl")] public Error control (ControlCommand cmd, uchar[] buffer); public Error info (ControlCommand what, ref uchar[] buffer); public Error encrypt (uchar[] out_buffer, uchar[] in_buffer); public Error decrypt (uchar[] out_buffer, uchar[] in_buffer); [CCode (cname = "gcry_cipher_setkey")] public Error set_key (uchar[] key_data); [CCode (cname = "gcry_cipher_setiv")] public Error set_iv (uchar[] iv_data); [CCode (cname = "gcry_cipher_setctr")] public Error set_counter_vector (uchar[] counter_vector); [CCode (cname = "gcry_cipher_gettag")] public Error get_tag(uchar[] out_buffer); [CCode (cname = "gcry_cipher_checktag")] public Error check_tag(uchar[] in_buffer); public Error reset (); public Error sync (); } } [Compact, CCode (cname = "struct gcry_md_handle", cprefix = "gcry_md_", free_function = "gcry_md_close")] public class Hash { [CCode (cname = "enum gcry_md_algos", cprefix = "GCRY_MD_")] public enum Algorithm { NONE, SHA1, RMD160, MD5, MD4, MD2, TIGER, TIGER1, TIGER2, HAVAL, SHA224, SHA256, SHA384, SHA512, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, CRC32, CRC32_RFC1510, CRC24_RFC2440, WHIRLPOOL, GOSTR3411_94, STRIBOG256, STRIBOG512; [CCode (cname = "gcry_md_get_algo_dlen")] public size_t get_digest_length (); [CCode (cname = "gcry_md_algo_info")] public Error info (ControlCommand what, ref uchar[] buffer); [CCode (cname = "gcry_md_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_md_map_name")] public static Algorithm from_string (string name); [CCode (cname = "gcry_md_test_algo")] public Error is_available (); [CCode (cname = "gcry_md_get_asnoid")] public Error get_oid (uchar[] buffer); } [CCode (cname = "enum gcry_md_flags", cprefix = "GCRY_MD_FLAG_")] public enum Flag { SECURE, HMAC, BUGEMU1 } public static Error open (out Hash hash, Algorithm algo, Flag flag); public void close (); public Error enable (Algorithm algo); [CCode (instance_pos = -1)] public Error copy (out Hash dst); public void reset (); [CCode (cname = "enum gcry_md_ctl")] public Error control (ControlCommand cmd, uchar[] buffer); public void write (uchar[] buffer); [CCode (array_length = false)] public unowned uchar[] read (Algorithm algo); public static void hash_buffer (Algorithm algo, [CCode (array_length = false)] uchar[] digest, uchar[] buffer); public Algorithm get_algo (); public bool is_enabled (Algorithm algo); public bool is_secure (); public Error info (ControlCommand what, uchar[] buffer); [CCode (cname = "gcry_md_setkey")] public Error set_key (uchar[] key_data); public void putc (char c); public void final (); public static Error list (ref Algorithm[] algos); } namespace Random { [CCode (cname = "gcry_random_level_t")] public enum Level { [CCode (cname = "GCRY_WEAK_RANDOM")] WEAK, [CCode (cname = "GCRY_STRONG_RANDOM")] STRONG, [CCode (cname = "GCRY_VERY_STRONG_RANDOM")] VERY_STRONG } [CCode (cname = "gcry_randomize")] public static void randomize (uchar[] buffer, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_fast_random_poll")] public static Error poll (); [CCode (cname = "gcry_random_bytes", array_length = false)] public static uchar[] random_bytes (size_t nbytes, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_random_bytes_secure")] public static uchar[] random_bytes_secure (size_t nbytes, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_create_nonce")] public static void nonce (uchar[] buffer); } [Compact, CCode (cname = "struct gcry_mpi", cprefix = "gcry_mpi_", free_function = "gcry_mpi_release")] public class MPI { [CCode (cname = "enum gcry_mpi_format", cprefix = "GCRYMPI_FMT_")] public enum Format { NONE, STD, PGP, SSH, HEX, USG } [CCode (cname = "enum gcry_mpi_flag", cprefix = "GCRYMPI_FLAG_")] public enum Flag { SECURE, OPAQUE } public MPI (uint nbits); [CCode (cname = "gcry_mpi_snew")] public MPI.secure (uint nbits); public MPI copy (); public void set (MPI u); public void set_ui (ulong u); public void swap (); public int cmp (MPI v); public int cmp_ui (ulong v); public static Error scan (out MPI ret, MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nscanned); [CCode (instance_pos = -1)] public Error print (MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nwritter); [CCode (instance_pos = -1)] public Error aprint (MPI.Format format, out uchar[] buffer); public void add (MPI u, MPI v); public void add_ui (MPI u, ulong v); public void addm (MPI u, MPI v, MPI m); public void sub (MPI u, MPI v); public void sub_ui (MPI u, MPI v); public void subm (MPI u, MPI v, MPI m); public void mul (MPI u, MPI v); public void mul_ui (MPI u, ulong v); public void mulm (MPI u, MPI v, MPI m); public void mul_2exp (MPI u, ulong cnt); public void div (MPI q, MPI r, MPI dividend, MPI divisor, int round); public void mod (MPI dividend, MPI divisor); public void powm (MPI b, MPI e, MPI m); public int gcd (MPI a, MPI b); public int invm (MPI a, MPI m); public uint get_nbits (); public int test_bit (uint n); public void set_bit (uint n); public void clear_bit (uint n); public void set_highbit (uint n); public void clear_highbit (uint n); public void rshift (MPI a, uint n); public void lshift (MPI a, uint n); public void set_flag (MPI.Flag flag); public void clear_flag (MPI.Flag flag); public int get_flag (MPI.Flag flag); } [Compact, CCode (cname = "struct gcry_sexp", free_function = "gcry_sexp_release")] public class SExp { [CCode (cprefix = "GCRYSEXP_FMT_")] public enum Format { DEFAULT, CANON, BASE64, ADVANCED } public static Error @new (out SExp retsexp, void * buffer, size_t length, int autodetect); public static Error create (out SExp retsexp, void * buffer, size_t length, int autodetect, GLib.DestroyNotify free_function); public static Error sscan (out SExp retsexp, out size_t erroff, char[] buffer); public static Error build (out SExp retsexp, out size_t erroff, string format, ...); public size_t sprint (Format mode, char[] buffer); public static size_t canon_len (uchar[] buffer, out size_t erroff, out int errcode); public SExp find_token (string token, size_t token_length = 0); public int length (); public SExp? nth (int number); public SExp? car (); public SExp? cdr (); public unowned char[] nth_data (int number); public gcry_string nth_string (int number); public MPI nth_mpi (int number, MPI.Format mpifmt); } [CCode (cname = "char", free_function = "gcry_free")] public class gcry_string : string { } [CCode (lower_case_cprefix = "gcry_pk_")] namespace PublicKey { [CCode (cname = "enum gcry_pk_algos")] public enum Algorithm { RSA, ELG_E, DSA, ELG, ECDSA; [CCode (cname = "gcry_pk_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_pk_map_name")] public static Algorithm map_name (string name); } public static Error encrypt (out SExp ciphertext, SExp data, SExp pkey); public static Error decrypt (out SExp plaintext, SExp data, SExp skey); public static Error sign (out SExp signature, SExp data, SExp skey); public static Error verify (SExp signature, SExp data, SExp pkey); public static Error testkey (SExp key); public static Error genkey (out SExp r_key, SExp s_params); public static uint get_nbits (SExp key); } [CCode (lower_case_cprefix = "gcry_kdf_")] namespace KeyDerivation { [CCode (cname = "gcry_kdf_algos", cprefix = "GCRY_KDF_", has_type_id = false)] public enum Algorithm { NONE, SIMPLE_S2K, SALTED_S2K, ITERSALTED_S2K, PBKDF1, PBKDF2, SCRYPT } public GCrypt.Error derive ([CCode (type = "const void*", array_length_type = "size_t")] uint8[] passphrasse, GCrypt.KeyDerivation.Algorithm algo, GCrypt.Hash.Algorithm subalgo, [CCode (type = "const void*", array_length_type = "size_t")] uint8[] salt, ulong iterations, [CCode (type = "void*", array_length_type = "size_t", array_length_pos = 5.5)] uint8[] keybuffer); } } dino-0.4.3/crypto-vala/vapi/libsrtp2.vapi0000644000000000000000000001030114452563620016771 0ustar rootroot[CCode (cheader_filename = "srtp2/srtp.h")] namespace Srtp { public const uint MAX_TRAILER_LEN; public static ErrorStatus init(); public static ErrorStatus shutdown(); [Compact] [CCode (cname = "srtp_ctx_t", cprefix = "srtp_", free_function = "srtp_dealloc")] public class Context { public static ErrorStatus create(out Context session, Policy? policy); public ErrorStatus protect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); public ErrorStatus unprotect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); public ErrorStatus protect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); public ErrorStatus unprotect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); public ErrorStatus add_stream(ref Policy policy); public ErrorStatus update_stream(ref Policy policy); public ErrorStatus remove_stream(uint ssrc); public ErrorStatus update(ref Policy policy); } [CCode (cname = "srtp_ssrc_t")] public struct Ssrc { public SsrcType type; public uint value; } [CCode (cname = "srtp_ssrc_type_t", cprefix = "ssrc_")] public enum SsrcType { undefined, specific, any_inbound, any_outbound } [CCode (cname = "srtp_policy_t", destroy_function = "")] public struct Policy { public Ssrc ssrc; public CryptoPolicy rtp; public CryptoPolicy rtcp; [CCode (array_length = false)] public uint8[] key; public ulong num_master_keys; public ulong window_size; [CCode (ctype = "int")] public bool allow_repeat_tx; [CCode (array_length_cname = "enc_xtn_hdr_count")] public int[] enc_xtn_hdr; public Policy* next; } [CCode (cname = "srtp_crypto_policy_t")] public struct CryptoPolicy { public CipherType cipher_type; public int cipher_key_len; public AuthType auth_type; public int auth_key_len; public int auth_tag_len; public SecurityServices sec_serv; public void set_aes_cm_128_hmac_sha1_80(); public void set_aes_cm_128_hmac_sha1_32(); public void set_aes_cm_128_null_auth(); public void set_aes_cm_192_hmac_sha1_32(); public void set_aes_cm_192_hmac_sha1_80(); public void set_aes_cm_192_null_auth(); public void set_aes_cm_256_hmac_sha1_32(); public void set_aes_cm_256_hmac_sha1_80(); public void set_aes_cm_256_null_auth(); public void set_aes_gcm_128_16_auth(); public void set_aes_gcm_128_8_auth(); public void set_aes_gcm_128_8_only_auth(); public void set_aes_gcm_256_16_auth(); public void set_aes_gcm_256_8_auth(); public void set_aes_gcm_256_8_only_auth(); public void set_null_cipher_hmac_null(); public void set_null_cipher_hmac_sha1_80(); public void set_rtp_default(); public void set_rtcp_default(); public void set_from_profile_for_rtp(Profile profile); public void set_from_profile_for_rtcp(Profile profile); } [CCode (cname = "srtp_profile_t", cprefix = "srtp_profile_")] public enum Profile { reserved, aes128_cm_sha1_80, aes128_cm_sha1_32, null_sha1_80, null_sha1_32, aead_aes_128_gcm, aead_aes_256_gcm } [CCode (cname = "srtp_cipher_type_id_t")] public struct CipherType : uint32 {} [CCode (cname = "srtp_auth_type_id_t")] public struct AuthType : uint32 {} [CCode (cname = "srtp_sec_serv_t", cprefix = "sec_serv_")] public enum SecurityServices { none, conf, auth, conf_and_auth; } [CCode (cname = "srtp_err_status_t", cprefix = "srtp_err_status_", has_type_id = false)] public enum ErrorStatus { ok, fail, bad_param, alloc_fail, dealloc_fail, init_fail, terminus, auth_fail, cipher_fail, replay_fail, replay_old, algo_fail, no_such_op, no_ctx, cant_check, key_expired, socket_err, signal_err, nonce_bad, read_fail, write_fail, parse_err, encode_err, semaphore_err, pfkey_err, bad_mki, pkt_idx_old, pkt_idx_adv } [CCode (cname = "srtp_log_level_t", cprefix = "srtp_log_level_", has_type_id = false)] public enum LogLevel { error, warning, info, debug } [CCode (cname = "srtp_log_handler_func_t")] public delegate void LogHandler(LogLevel level, string msg); public static ErrorStatus install_log_handler(LogHandler func); }dino-0.4.3/dino.doap0000644000000000000000000010326014452563620012754 0ustar rootroot Dino dino Modern XMPP Chat Client 現代化的 XMPP 用戶端聊天軟件 现代 XMPP 聊天客户端 Modern XMPP Sohbet İstemcisi Modern XMPP-chattklient Klient Modern Fjalosjesh XMPP Современный XMPP клиент Client XMPP de discuții modern Cliente de Chat XMPP Moderno Moderno cliente de chat XMPP Nowoczesny komunikator XMPP Client XMPP modèrn Modernen XMPP-chatcliënt Een moderne XMPP-chatclient Moderne XMPP-sludreklient Šiuolaikinė XMPP pokalbių kliento programa Modernen XMPP Chat Client 現代的な XMPP チャット クライアント Client di chat moderno per XMPP Un modern client de conversationes XMPP Aplikasi chat XMPP modern Modern XMPP csevegőprogram Cliente moderno para conversas XMPP Client de clavardage XMPP moderne کلاینت نوین گپ XMPP XMPP txat bezero modernoa Un cliente de XMPP moderno Moderna XMPP-Retebabililo Modernes XMPP-Chat-Programm Moderní XMPP klient Client de xat XMPP modern تطبيق حديث للدردشة عبر XMPP Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind. It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications. Dino fetches history from the server and synchronizes messages with other devices. Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。 它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已讀回條和正在輸入提示。 Dino 從伺服器取得訊息並與其他裝置同步。 Dino 是一个现代的开源聊天桌面客户端。它致力于提供一个清爽又可靠的 Jabber/XMPP 体验,同时又保护您的隐私。 它支持 OMEMO 和 OpenPGP 端对端加密并允许配置隐私相关的特性比如已读回执和输入提醒。 Dino 从服务器获取消息并和其他设备同步。 Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır. OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve "okundu" bilgisi, "yazıyor..." bildirimi gibi gizlilikle alakalı özelliklerin ayarlanabilmesini sağlar. Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara senkronize eder. Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser efter din integritet. Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter konfigurering av funktioner med integritetspåverkan som läsbekräftelser och skriftaviseringar. Dino hämtar historik från servern och synkroniserar meddelanden med andra enheter. Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj. Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime shtypjeje. Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje të tjera. Dino - это современный клиент для чатов с открытым исходным кодом, направленный на надёжное и приватное использование Jabber/XMPP на персональных компьютерах. Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет настраивать такие функции, как уведомления о прочтении и наборе сообщений. Dino загружает историю с сервера и синхронизирует сообщения с другими устройствами. Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, ținând cont de confidențialitatea dumneavoastră. Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, și permite configurarea caracteristicilor legate de confidențialitate precum trimiterea notificărilor de primire și tastare. Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu celelalte dispozitive. Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em oferecer uma experiência Jabber/XMPP transparente e confiável levando em consideração a sua privacidade. Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, permite configurar funcionalidades relativas à privacidade como notificações de leitura, recebimento e escrita. Dino obtém o histórico do servidor e sincroniza mensagens com outros dispositivos. Dino é um moderno chat de código aberto para desktop. Ele é focado em prover uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua privacidade. Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar privacidade—características relacionadas às notificações de leitura, recebimento e escrita. Dino obtém o histórico do servidor e sincroniza mensagens com outros aparelhos. Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej obsłudze sieci Jabber/XMPP dbając o twoją prywatność. Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o pisaniu czy odczytaniu wiadomości. Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi urządzeniami. Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte vòstra confidencialitat. Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de configurar de foncionalitats ligadas amb la confidencialitat coma los acusats de lectura e las notificacions d’escritura. Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres periferics. Dino is een moderne, vrije chattoepassing voor uw bureaublad. Ze biedt een eenvoudige en betrouwbare Jabber/XMPP-ervaring, met uw privacy in het achterhoofd. Ze ondersteunt eind-tot-eind-versleuteling met OMEMO en OpenPGP, en laat u toe privacygerelateerde functies, gelijk leesbevestigingen en typmeldingen, in te stellen. Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten. Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het achterhoofd. Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-typenmeldingen, in te stellen. Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten. Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om personvernet. Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og skrivevarsling. Dino henter historikk fra tjeneren og synkroniserer meldinger med andre enheter. Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą nepamirštant apie jūsų privatumą. Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie žinučių skaitymą ir rašymą. Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais įrenginiais. Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op Privatsphär. Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen. Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat anere Geräter. Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プライバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供を第一に考えて開発されています。 OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送信や入力通知などのプライバシー関連の設定も可能です。 Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。 Dino è un client di chat per il desktop, moderno e open-source. Si concentra nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente la tua privacy. Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di configurare le funzioni relative alla privacy come le ricevute di lettura e le notifiche di digitazione. Dino recupera la cronologia dal server e sincronizza i messaggi con gli altri dispositivi. Dino es un modern cliente de conversationes con fonte apert. It foca se sur provider un nett e fidibil experientie de Jabber/XMPP con un attention a confidentialitá. It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar sensitiv functiones quam confirmation de lectada e notificationes de tippada. Dino obtene li diarium del servitore e sincronisa missages inter altri apparates. Dino adalah aplikasi chat open source modern untuk PC. Menyediakan pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda. Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan pengaturan fitur terkait privasi seperti tanda pesan dibaca dan pemberitahuan pengetikan. Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan perangkat lain. A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, miközben a magánszféra megőrzését is fontosnak tartja. Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint például az olvasási visszaigazolást és a gépelési értesítéseket. A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az üzeneteket a többi eszközzel. Dino é un cliente moderno e de código aberto para o escritorio. Orientado a fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e seguranza presentes. Suporta o cifrado de punto-a-punto con OMEMO e OpenPGP e permite configurar trazos orientados á privacidade tales coma confirmación de lectura e notificacións de escritura. Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros dispositivos. Dino est un client de clavardage libre et moderne pour le bureau. Il se concentre sur la fourniture d’une expérience XMPP simple et fiable tout en ayant toujours à l’esprit votre confidentialité. Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et permet de configurer les fonctions liées à la confidentialité telles que les accusés de réception et les notifications d’écriture. Dino récupère l’historique du serveur et synchronise les messages avec les autres clients. Dino on nykyaikainen avoimen lähdekoodin jutteluohjelma työpöydälle. Se keskittyy tarjoamaan selkeän ja luotettavan Jabber/XMPP-kokemuksen unohtamatta yksityisyyttäsi. Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja kirjoitusilmoitusten asetusten määrittämisen. Dino hakee historian palvelimelta ja synkronisoi viestit muiden laitteiden kanssa. دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد. از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و اعلان در حال نوشتن بودن. دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی می‌کند. Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan hartzeaz gain. Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta idazketa jakinarazpenak bezala. Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak sinkronizatzen ditu. Dino es un cliente de mensajería moderno y libre para escritorio y móvil. Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo la privacidad en mente. Soporta encriptación fin-a-fin a través de OMEMO y OpenPGP y permite configurar las características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura. Dino recupera el historial de mensajes desde el servidor y sincroniza los mensajes con otros dispositivos. Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian privatecon. Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado. Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj aparatoj. Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen Schwerpunkt auf Privatsphäre. Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und enthält Privatsphäre-Einstellungen zu Lesebestätigungen und Tippbenachrichtigungen. Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit anderen Geräten. Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na zachování vašeho soukromí. Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení a oznámení o psaní. Dino načítá historii ze serveru a synchronizuje zprávy s ostatními zařízeními. Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en compte la vostra privacitat. Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar funcionalitats relacionades amb la privacitat com per exemple rebuts de lectura i notificacions d'escriptura. Dino recupera l'historial del servidor i sincronitza els missatges amb altres dispositius. إنّ Dino برنامج عصري ومفتوح المصدر للدردشة صُمّم لسطح المكتب. ويُركّز علي تقديم تجربة نظيفة وموثوق منها لجابر/XMPP مع أخذ خصوصيتكم بعين الإعتبار. وهو يدعم التشفير بواسطة OMEMO و OpenPGP يسمح بإعداد ميزات الخصوصية كالإيصالات المقروءة والإخطارات عند الكتابة. يقوم Dino بجلب السِجلّ مِن السيرفر ثم يُزامِن الرسائل مع الأجهزة الأخرى. Vala Linux FreeBSD complete complete complete partial complete For use with XEP-0261 deprecated Migrating to XEP-0402 if supported by server complete partial Only for viewing avatars partial For use with XEP-0313 partial partial For use with XEP-0260 complete For file transfers using XEP-0363 complete complete complete complete complete deprecated Only to fetch Avatars from other users complete partial partial partial complete complete complete complete complete partial complete complete partial 1.0 complete partial No support for sending complete complete complete partial partial complete For use with XEP-0280 complete 1.2.0 0.2 partial Not for MUCs complete 1.0.0 complete partial complete 0.3.1 complete complete complete partial Only for outgoing messages complete 0.3.0 partial partial complete complete complete complete partial No support for embedded thumbnails dino-0.4.3/dino.doap.in0000644000000000000000000003542714452563620013372 0ustar rootroot Dino dino Modern XMPP Chat Client Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind. It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications. Dino fetches history from the server and synchronizes messages with other devices. Vala Linux FreeBSD complete complete complete partial complete For use with XEP-0261 deprecated Migrating to XEP-0402 if supported by server complete partial Only for viewing avatars partial For use with XEP-0313 partial partial For use with XEP-0260 complete For file transfers using XEP-0363 complete complete complete complete complete deprecated Only to fetch Avatars from other users complete partial partial partial complete complete complete complete complete partial complete complete partial 1.0 complete partial No support for sending complete complete complete partial partial complete For use with XEP-0280 complete 1.2.0 0.2 partial Not for MUCs complete 1.0.0 complete partial complete 0.3.1 complete complete complete partial Only for outgoing messages complete 0.3.0 partial partial complete complete complete complete partial No support for embedded thumbnails dino-0.4.3/libdino/0000755000000000000000000000000014452563620012574 5ustar rootrootdino-0.4.3/libdino/CMakeLists.txt0000644000000000000000000000736714452563620015351 0ustar rootrootfind_packages(LIBDINO_PACKAGES REQUIRED GDKPixbuf2 Gee GLib GModule GObject ) vala_precompile(LIBDINO_VALA_C SOURCES src/application.vala src/dbus/login1.vala src/dbus/notifications.vala src/dbus/upower.vala src/entity/account.vala src/entity/call.vala src/entity/conversation.vala src/entity/encryption.vala src/entity/file_transfer.vala src/entity/message.vala src/entity/settings.vala src/plugin/interfaces.vala src/plugin/loader.vala src/plugin/registry.vala src/service/avatar_manager.vala src/service/blocking_manager.vala src/service/call_store.vala src/service/call_state.vala src/service/call_peer_state.vala src/service/calls.vala src/service/chat_interaction.vala src/service/connection_manager.vala src/service/content_item_store.vala src/service/conversation_manager.vala src/service/counterpart_interaction_manager.vala src/service/database.vala src/service/entity_capabilities_storage.vala src/service/entity_info.vala src/service/fallback_body.vala src/service/file_manager.vala src/service/file_transfer_storage.vala src/service/history_sync.vala src/service/jingle_file_transfers.vala src/service/message_correction.vala src/service/message_processor.vala src/service/message_storage.vala src/service/module_manager.vala src/service/muc_manager.vala src/service/notification_events.vala src/service/presence_manager.vala src/service/replies.vala src/service/reactions.vala src/service/registration.vala src/service/roster_manager.vala src/service/search_processor.vala src/service/stream_interactor.vala src/service/util.vala src/util/display_name.vala src/util/util.vala src/util/weak_map.vala CUSTOM_VAPIS "${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi" "${CMAKE_BINARY_DIR}/exports/qlite.vapi" CUSTOM_DEPS xmpp-vala qlite PACKAGES ${LIBDINO_PACKAGES} GENERATE_VAPI dino GENERATE_HEADER dino ) add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/dino_i18n.h" COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/src/dino_i18n.h" "${CMAKE_BINARY_DIR}/exports/dino_i18n.h" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/dino_i18n.h" COMMENT Copy header file dino_i18n.h ) add_custom_target(dino-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/dino.deps ${CMAKE_BINARY_DIR}/exports/dino_i18n.h ) add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" -DDINO_VERSION=\"${PROJECT_VERSION}\") add_library(libdino SHARED ${LIBDINO_VALA_C} ${CMAKE_BINARY_DIR}/exports/dino_i18n.h) add_dependencies(libdino dino-vapi) target_link_libraries(libdino xmpp-vala qlite ${LIBDINO_PACKAGES} m) set_target_properties(libdino PROPERTIES PREFIX "" VERSION 0.0 SOVERSION 0) install(TARGETS libdino ${TARGET_INSTALL}) install(FILES ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/dino.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/dino.h ${CMAKE_BINARY_DIR}/exports/dino_i18n.h DESTINATION ${INCLUDE_INSTALL_DIR}) if(BUILD_TESTS) vala_precompile(LIBDINO_TEST_VALA_C SOURCES "tests/weak_map.vala" "tests/testcase.vala" "tests/common.vala" CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/dino_internal.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi PACKAGES ${LIBDINO_PACKAGES} OPTIONS ${LIBDINO_EXTRA_OPTIONS} ) add_definitions(${VALA_CFLAGS}) add_executable(libdino-test ${LIBDINO_TEST_VALA_C}) target_link_libraries(libdino-test libdino) endif(BUILD_TESTS) dino-0.4.3/libdino/src/0000755000000000000000000000000014452563620013363 5ustar rootrootdino-0.4.3/libdino/src/application.vala0000644000000000000000000001370514452563620016541 0ustar rootrootusing Dino.Entities; namespace Dino { extern const string VERSION; public string get_version() { return VERSION; } public string get_short_version() { if (!VERSION.contains("~")) return VERSION; return VERSION.split("~")[0] + "+"; } public interface Application : GLib.Application { public abstract Database db { get; set; } public abstract Dino.Entities.Settings settings { get; set; } public abstract StreamInteractor stream_interactor { get; set; } public abstract Plugins.Registry plugin_registry { get; set; } public abstract SearchPathGenerator? search_path_generator { get; set; } internal static string print_xmpp; private const OptionEntry[] options = { { "print-xmpp", 0, 0, OptionArg.STRING, ref print_xmpp, "Print XMPP stanzas identified by DESC to stderr", "DESC" }, { null } }; public abstract void handle_uri(string jid, string query, Gee.Map options); public void init() throws Error { if (DirUtils.create_with_parents(get_storage_dir(), 0700) == -1) { throw new Error(-1, 0, "Could not create storage dir \"%s\": %s", get_storage_dir(), FileUtils.error_from_errno(errno).to_string()); } this.db = new Database(Path.build_filename(get_storage_dir(), "dino.db")); this.settings = new Dino.Entities.Settings.from_db(db); this.stream_interactor = new StreamInteractor(db); MessageProcessor.start(stream_interactor, db); MessageStorage.start(stream_interactor, db); PresenceManager.start(stream_interactor); CounterpartInteractionManager.start(stream_interactor); BlockingManager.start(stream_interactor); ConversationManager.start(stream_interactor, db); MucManager.start(stream_interactor); AvatarManager.start(stream_interactor, db); RosterManager.start(stream_interactor, db); FileManager.start(stream_interactor, db); Calls.start(stream_interactor, db); CallStore.start(stream_interactor, db); ContentItemStore.start(stream_interactor, db); ChatInteraction.start(stream_interactor); NotificationEvents.start(stream_interactor); SearchProcessor.start(stream_interactor, db); Register.start(stream_interactor, db); EntityInfo.start(stream_interactor, db); MessageCorrection.start(stream_interactor, db); FileTransferStorage.start(stream_interactor, db); Reactions.start(stream_interactor, db); Replies.start(stream_interactor, db); FallbackBody.start(stream_interactor, db); create_actions(); startup.connect(() => { stream_interactor.connection_manager.log_options = print_xmpp; restore(); }); shutdown.connect(() => { stream_interactor.connection_manager.make_offline_all(); }); open.connect((files, hint) => { if (files.length != 1) { warning("Can't handle more than one URI at once."); return; } File file = files[0]; if (!file.has_uri_scheme("xmpp")) { warning("xmpp:-URI expected"); return; } string uri = file.get_uri(); if (!uri.contains(":")) { warning("Invalid URI"); return; } string r = uri.split(":", 2)[1]; string[] m = r.split("?", 2); string jid = m[0]; while (jid[0] == '/') { jid = jid.substring(1); } jid = Uri.unescape_string(jid); try { jid = new Xmpp.Jid(jid).to_string(); } catch (Xmpp.InvalidJidError e) { warning("Received invalid jid in xmpp:-URI: %s", e.message); } string query = "message"; Gee.Map options = new Gee.HashMap(); if (m.length == 2) { string[] cmds = m[1].split(";"); query = cmds[0]; for (int i = 1; i < cmds.length; ++i) { string[] opt = cmds[i].split("=", 2); options[Uri.unescape_string(opt[0])] = opt.length == 2 ? Uri.unescape_string(opt[1]) : ""; } } activate(); handle_uri(jid, query, options); }); add_main_option_entries(options); } public static string get_storage_dir() { return Path.build_filename(Environment.get_user_data_dir(), "dino"); } public static unowned Application get_default() { return (Dino.Application) GLib.Application.get_default(); } public void create_actions() { SimpleAction accept_subscription_action = new SimpleAction("accept-subscription", VariantType.INT32); accept_subscription_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; stream_interactor.get_module(PresenceManager.IDENTITY).approve_subscription(conversation.account, conversation.counterpart); stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(conversation.account, conversation.counterpart); }); add_action(accept_subscription_action); } protected void add_connection(Account account) { if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) hold(); stream_interactor.connect_account(account); } protected void remove_connection(Account account) { if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) release(); stream_interactor.disconnect_account.begin(account); } private void restore() { foreach (Account account in db.get_accounts()) { if (account.enabled) add_connection(account); } } } } dino-0.4.3/libdino/src/dbus/0000755000000000000000000000000014452563620014320 5ustar rootrootdino-0.4.3/libdino/src/dbus/login1.vala0000644000000000000000000000065214452563620016361 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.login1.Manager")] public interface Login1Manager : Object { public signal void PrepareForSleep(bool suspend); } public static async Login1Manager? get_login1() { try { return yield Bus.get_proxy(BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1"); } catch (IOError e) { stderr.printf("%s\n", e.message); } return null; } }dino-0.4.3/libdino/src/dbus/notifications.vala0000644000000000000000000000237514452563620020045 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.Notifications")] public interface DBusNotifications : GLib.Object { public signal void action_invoked(uint32 key, string action_key); public signal void notification_closed (uint32 id, uint32 reason); public abstract async uint32 notify(string app_name, uint32 replaces_id, string app_icon, string summary, string body, string[] actions, HashTable hints, int32 expire_timeout) throws DBusError, IOError; public abstract async void get_capabilities(out string[] capabilities) throws Error; public abstract async void close_notification(uint id) throws DBusError, IOError; public abstract async void get_server_information(out string name, out string vendor, out string version, out string spec_version) throws DBusError, IOError; } public static async DBusNotifications? get_notifications_dbus() { try { return yield Bus.get_proxy(BusType.SESSION, "org.freedesktop.Notifications", "/org/freedesktop/Notifications"); } catch (IOError e) { warning("Couldn't get org.freedesktop.Notifications DBus instance: %s\n", e.message); } return null; } }dino-0.4.3/libdino/src/dbus/upower.vala0000644000000000000000000000067514452563620016516 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.UPower")] public interface UPower : Object { public signal void Sleeping(); public signal void Resuming(); } public static UPower? get_upower() { UPower? upower = null; try { upower = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower", "/org/freedesktop/UPower"); } catch (IOError e) { stderr.printf ("%s\n", e.message); } return upower; } }dino-0.4.3/libdino/src/dino_i18n.h0000644000000000000000000000040514452563620015323 0ustar rootroot#ifndef __DINO_I18N_H__ #define __DINO_I18N_H__ #include #define dino_gettext(MsgId) ((char *) dgettext (GETTEXT_PACKAGE, MsgId)) #define dino_ngettext(MsgId, MsgIdPlural, Int) ((char *) dngettext (GETTEXT_PACKAGE, MsgId, MsgIdPlural, Int)) #endifdino-0.4.3/libdino/src/entity/0000755000000000000000000000000014452563620014677 5ustar rootrootdino-0.4.3/libdino/src/entity/account.vala0000644000000000000000000001037014452563620017201 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Entities { public class Account : Object { public int id { get; set; } public string localpart { get { return full_jid.localpart; } } public string domainpart { get { return full_jid.domainpart; } } public string resourcepart { get { return full_jid.resourcepart;} } public Jid bare_jid { owned get { return full_jid.bare_jid; } } public Jid full_jid { get; private set; } public string? password { get; set; } public string display_name { owned get { return (alias != null && alias.length > 0) ? alias.dup() : bare_jid.to_string(); } } public string? alias { get; set; } public bool enabled { get; set; default = false; } public string? roster_version { get; set; } public DateTime mam_earliest_synced { get; set; default=new DateTime.from_unix_utc(0); } private Database? db; public Account(Jid bare_jid, string? resourcepart, string? password, string? alias) { this.id = -1; if (resourcepart != null) { try { this.full_jid = bare_jid.with_resource(resourcepart); } catch (InvalidJidError e) { warning("Tried to create account with invalid resource (%s), defaulting to auto generated", e.message); } } if (this.full_jid == null) { try { this.full_jid = bare_jid.with_resource("dino." + Random.next_int().to_string("%x")); } catch (InvalidJidError e) { error("Auto-generated resource was invalid (%s)", e.message); } } this.password = password; this.alias = alias; } public Account.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.account.id]; full_jid = new Jid(row[db.account.bare_jid]).with_resource(row[db.account.resourcepart]); password = row[db.account.password]; alias = row[db.account.alias]; enabled = row[db.account.enabled]; roster_version = row[db.account.roster_version]; mam_earliest_synced = new DateTime.from_unix_utc(row[db.account.mam_earliest_synced]); notify.connect(on_update); } public void persist(Database db) { if (id > 0) return; this.db = db; id = (int) db.account.insert() .value(db.account.bare_jid, bare_jid.to_string()) .value(db.account.resourcepart, resourcepart) .value(db.account.password, password) .value(db.account.alias, alias) .value(db.account.enabled, enabled) .value(db.account.roster_version, roster_version) .value(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()) .perform(); notify.connect(on_update); } public void remove() { db.account.delete().with(db.account.bare_jid, "=", bare_jid.to_string()).perform(); notify.disconnect(on_update); id = -1; db = null; } public bool equals(Account acc) { return equals_func(this, acc); } public static bool equals_func(Account acc1, Account acc2) { return acc1.bare_jid.to_string() == acc2.bare_jid.to_string(); } public static uint hash_func(Account acc) { return acc.bare_jid.to_string().hash(); } private void on_update(Object o, ParamSpec sp) { var update = db.account.update().with(db.account.id, "=", id); switch (sp.name) { case "bare-jid": update.set(db.account.bare_jid, bare_jid.to_string()); break; case "resourcepart": update.set(db.account.resourcepart, resourcepart); break; case "password": update.set(db.account.password, password); break; case "alias": update.set(db.account.alias, alias); break; case "enabled": update.set(db.account.enabled, enabled); break; case "roster-version": update.set(db.account.roster_version, roster_version); break; case "mam-earliest-synced": update.set(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()); break; } update.perform(); } } } dino-0.4.3/libdino/src/entity/call.vala0000644000000000000000000001523214452563620016462 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class Call : Object { public const bool DIRECTION_OUTGOING = true; public const bool DIRECTION_INCOMING = false; public enum State { RINGING, ESTABLISHING, IN_PROGRESS, OTHER_DEVICE, ENDED, DECLINED, MISSED, FAILED } public int id { get; set; default=-1; } public Account account { get; set; } public Jid counterpart { get; set; } public Gee.List counterparts = new Gee.ArrayList(Jid.equals_bare_func); public Jid ourpart { get; set; } public Jid proposer { get { return direction == DIRECTION_OUTGOING ? ourpart : counterpart; } } public bool direction { get; set; } public DateTime time { get; set; } public DateTime local_time { get; set; } public DateTime end_time { get; set; } public Encryption encryption { get; set; default=Encryption.NONE; } public State state { get; set; } private Database? db; public Call.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.call.id]; account = db.get_account_by_id(row[db.call.account_id]); string our_resource = row[db.call.our_resource]; if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.call.direction]; time = new DateTime.from_unix_utc(row[db.call.time]); local_time = new DateTime.from_unix_utc(row[db.call.local_time]); end_time = new DateTime.from_unix_utc(row[db.call.end_time]); encryption = (Encryption) row[db.call.encryption]; state = (State) row[db.call.state]; Qlite.QueryBuilder counterparts_select = db.call_counterpart.select().with(db.call_counterpart.call_id, "=", id); foreach (Qlite.Row counterparts_row in counterparts_select) { Jid peer = db.get_jid_by_id(counterparts_row[db.call_counterpart.jid_id]); if (!counterparts.contains(peer)) { // Legacy: The first peer is also in the `call` table. Don't add twice. counterparts.add(peer); } } counterpart = db.get_jid_by_id(row[db.call.counterpart_id]); string counterpart_resource = row[db.call.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); if (counterparts.is_empty) { counterparts.add(counterpart); } notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.call.insert() .value(db.call.account_id, account.id) .value(db.call.our_resource, ourpart.resourcepart) .value(db.call.direction, direction) .value(db.call.time, (long) time.to_unix()) .value(db.call.local_time, (long) local_time.to_unix()) .value(db.call.encryption, encryption) .value(db.call.state, State.ENDED); // No point in persisting states that can't survive a restart if (end_time != null) { builder.value(db.call.end_time, (long) end_time.to_unix()); } else { builder.value(db.call.end_time, (long) local_time.to_unix()); } if (counterpart != null) { builder.value(db.call.counterpart_id, db.get_jid_id(counterpart)) .value(db.call.counterpart_resource, counterpart.resourcepart); } id = (int) builder.perform(); foreach (Jid peer in counterparts) { db.call_counterpart.insert() .value(db.call_counterpart.call_id, id) .value(db.call_counterpart.jid_id, db.get_jid_id(peer)) .value(db.call_counterpart.resource, peer.resourcepart) .perform(); } notify.connect(on_update); } public void add_peer(Jid peer) { if (counterparts.contains(peer)) return; counterparts.add(peer); if (db != null) { db.call_counterpart.insert() .value(db.call_counterpart.call_id, id) .value(db.call_counterpart.jid_id, db.get_jid_id(peer)) .value(db.call_counterpart.resource, peer.resourcepart) .perform(); } } public bool equals(Call c) { return equals_func(this, c); } public static bool equals_func(Call c1, Call c2) { if (c1.id == c2.id) { return true; } return false; } public static uint hash_func(Call call) { return (uint)call.id; } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.call.update().with(db.call.id, "=", id); switch (sp.name) { case "counterpart": update_builder.set(db.call.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.call.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.call.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.call.direction, direction); break; case "time": update_builder.set(db.call.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.call.local_time, (long) local_time.to_unix()); break; case "end-time": update_builder.set(db.call.end_time, (long) end_time.to_unix()); break; case "encryption": update_builder.set(db.call.encryption, encryption); break; case "state": // No point in persisting states that can't survive a restart if (state == State.RINGING || state == State.ESTABLISHING || state == State.IN_PROGRESS) return; update_builder.set(db.call.state, state); break; } update_builder.perform(); } } } dino-0.4.3/libdino/src/entity/conversation.vala0000644000000000000000000002116314452563620020261 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class Conversation : Object { public signal void object_updated(Conversation conversation); public enum Type { CHAT, GROUPCHAT, GROUPCHAT_PM; public bool is_muc_semantic() { return this == GROUPCHAT || this == GROUPCHAT_PM; } } public int id { get; set; } public Type type_ { get; set; } public Account account { get; private set; } public Jid counterpart { get; private set; } public string? nickname { get; set; } public bool active { get; set; default = false; } public DateTime active_last_changed { get; private set; } private DateTime? _last_active; public DateTime? last_active { get { return _last_active; } set { if (_last_active == null || (value != null && value.difference(_last_active) > 0)) { _last_active = value; } } } public Encryption encryption { get; set; default = Encryption.NONE; } public Message? read_up_to { get; set; } public int read_up_to_item { get; set; default=-1; } public enum NotifySetting { DEFAULT, ON, OFF, HIGHLIGHT } public NotifySetting notify_setting { get; set; default = NotifySetting.DEFAULT; } public enum Setting { DEFAULT, ON, OFF } public Setting send_typing { get; set; default = Setting.DEFAULT; } public Setting send_marker { get; set; default = Setting.DEFAULT; } public int pinned { get; set; default = 0; } private Database? db; public Conversation(Jid jid, Account account, Type type) { this.account = account; this.counterpart = jid; this.type_ = type; } public Conversation.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.conversation.id]; type_ = (Conversation.Type) row[db.conversation.type_]; account = db.get_account_by_id(row[db.conversation.account_id]); string? resource = row[db.conversation.resource]; counterpart = db.get_jid_by_id(row[db.conversation.jid_id]); if (type_ == Conversation.Type.GROUPCHAT_PM) counterpart = counterpart.with_resource(resource); nickname = type_ == Conversation.Type.GROUPCHAT ? resource : null; active = row[db.conversation.active]; active_last_changed = new DateTime.from_unix_utc(row[db.conversation.active_last_changed]); int64? last_active = row[db.conversation.last_active]; if (last_active != null) this.last_active = new DateTime.from_unix_utc(last_active); encryption = (Encryption) row[db.conversation.encryption]; int? read_up_to = row[db.conversation.read_up_to]; if (read_up_to != null) this.read_up_to = db.get_message_by_id(read_up_to); read_up_to_item = row[db.conversation.read_up_to_item]; notify_setting = (NotifySetting) row[db.conversation.notification]; send_typing = (Setting) row[db.conversation.send_typing]; send_marker = (Setting) row[db.conversation.send_marker]; pinned = row[db.conversation.pinned]; notify.connect(on_update); } public void persist(Database db) { this.db = db; this.active_last_changed = new DateTime.now_utc(); var insert = db.conversation.insert() .value(db.conversation.account_id, account.id) .value(db.conversation.jid_id, db.get_jid_id(counterpart)) .value(db.conversation.type_, type_) .value(db.conversation.encryption, encryption) .value(db.conversation.active, active) .value(db.conversation.active_last_changed, (long) active_last_changed.to_unix()) .value(db.conversation.notification, notify_setting) .value(db.conversation.send_typing, send_typing) .value(db.conversation.send_marker, send_marker) .value(db.conversation.pinned, pinned); if (read_up_to != null) { insert.value(db.conversation.read_up_to, read_up_to.id); } if (read_up_to_item != -1) { insert.value(db.conversation.read_up_to_item, read_up_to_item); } if (nickname != null) { insert.value(db.conversation.resource, nickname); } if (counterpart.is_full()) { insert.value(db.conversation.resource, counterpart.resourcepart); } if (last_active != null) { insert.value(db.conversation.last_active, (long) last_active.to_unix()); } id = (int) insert.perform(); notify.connect(on_update); } public NotifySetting get_notification_setting(StreamInteractor stream_interactor) { return notify_setting != NotifySetting.DEFAULT ? notify_setting : get_notification_default_setting(stream_interactor); } public NotifySetting get_notification_default_setting(StreamInteractor stream_interactor) { if (!Application.get_default().settings.notifications) return NotifySetting.OFF; if (type_ == Type.GROUPCHAT) { if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(this.account, this.counterpart)) { return NotifySetting.ON; } else { return NotifySetting.HIGHLIGHT; } } return NotifySetting.ON; } public Setting get_send_typing_setting(StreamInteractor stream_interactor) { if (send_typing != Setting.DEFAULT) return send_typing; if (stream_interactor.get_module(MucManager.IDENTITY).is_public_room(this.account, this.counterpart)) return Setting.OFF; return Application.get_default().settings.send_typing ? Setting.ON : Setting.OFF; } public Setting get_send_marker_setting(StreamInteractor stream_interactor) { if (send_marker != Setting.DEFAULT) return send_marker; if (stream_interactor.get_module(MucManager.IDENTITY).is_public_room(this.account, this.counterpart)) return Setting.OFF; return Application.get_default().settings.send_marker ? Setting.ON : Setting.OFF; } public bool equals(Conversation? conversation) { if (conversation == null) return false; return equals_func(this, conversation); } public static bool equals_func(Conversation conversation1, Conversation conversation2) { return conversation1.counterpart.equals(conversation2.counterpart) && conversation1.account.equals(conversation2.account) && conversation1.type_ == conversation2.type_; } public static uint hash_func(Conversation conversation) { return conversation.counterpart.to_string().hash() ^ conversation.account.bare_jid.to_string().hash(); } private void on_update(Object o, ParamSpec sp) { var update = db.conversation.update().with(db.conversation.id, "=", id); switch (sp.name) { case "type-": update.set(db.conversation.type_, type_); break; case "encryption": update.set(db.conversation.encryption, encryption); break; case "read-up-to": if (read_up_to != null) { update.set(db.conversation.read_up_to, read_up_to.id); } else { update.set_null(db.conversation.read_up_to); } break; case "read-up-to-item": if (read_up_to_item != -1) { update.set(db.conversation.read_up_to_item, read_up_to_item); } else { update.set_null(db.conversation.read_up_to_item); } break; case "nickname": update.set(db.conversation.resource, nickname); break; case "active": update.set(db.conversation.active, active); update.set(db.conversation.active_last_changed, (long) new DateTime.now_utc().to_unix()); break; case "last-active": if (last_active != null) { update.set(db.conversation.last_active, (long) last_active.to_unix()); } else { update.set_null(db.conversation.last_active); } break; case "notify-setting": update.set(db.conversation.notification, notify_setting); break; case "send-typing": update.set(db.conversation.send_typing, send_typing); break; case "send-marker": update.set(db.conversation.send_marker, send_marker); break; case "pinned": update.set(db.conversation.pinned, pinned); break; } update.perform(); } } } dino-0.4.3/libdino/src/entity/encryption.vala0000644000000000000000000000035014452563620017734 0ustar rootrootnamespace Dino.Entities { public enum Encryption { NONE, PGP, OMEMO, DTLS_SRTP, SRTP, UNKNOWN; public bool is_some() { return this != NONE; } } }dino-0.4.3/libdino/src/entity/file_transfer.vala0000644000000000000000000001572214452563620020376 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class FileTransfer : Object { public const bool DIRECTION_SENT = true; public const bool DIRECTION_RECEIVED = false; public enum State { COMPLETE, IN_PROGRESS, NOT_STARTED, FAILED } public int id { get; set; default=-1; } public Account account { get; set; } public Jid counterpart { get; set; } public Jid ourpart { get; set; } public Jid? from { get { return direction == DIRECTION_SENT ? ourpart : counterpart; } } public Jid? to { get { return direction == DIRECTION_SENT ? counterpart : ourpart; } } public bool direction { get; set; } public DateTime time { get; set; } public DateTime? local_time { get; set; } public Encryption encryption { get; set; default=Encryption.NONE; } private InputStream? input_stream_ = null; public InputStream input_stream { get { if (input_stream_ == null) { File file = File.new_for_path(Path.build_filename(storage_dir, path ?? file_name)); try { input_stream_ = file.read(); } catch (Error e) { } } return input_stream_; } set { input_stream_ = value; } } private string file_name_; public string file_name { get { return file_name_; } set { file_name_ = Path.get_basename(value); if (file_name_ == Path.DIR_SEPARATOR_S || file_name_ == ".") { file_name_ = "unknown filename"; } else if (file_name_.has_prefix(".")) { file_name_ = "_" + file_name_; } } } private string? server_file_name_ = null; public string server_file_name { get { return server_file_name_ ?? file_name; } set { server_file_name_ = value; } } public string path { get; set; } public string? mime_type { get; set; } // TODO(hrxi): expand to 64 bit public int size { get; set; default=-1; } public State state { get; set; default=State.NOT_STARTED; } public int provider { get; set; } public string info { get; set; } public Cancellable cancellable { get; default=new Cancellable(); } private Database? db; private string storage_dir; public FileTransfer.from_row(Database db, Qlite.Row row, string storage_dir) throws InvalidJidError { this.db = db; this.storage_dir = storage_dir; id = row[db.file_transfer.id]; account = db.get_account_by_id(row[db.file_transfer.account_id]); // TODO don’t have to generate acc new counterpart = db.get_jid_by_id(row[db.file_transfer.counterpart_id]); string counterpart_resource = row[db.file_transfer.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); string our_resource = row[db.file_transfer.our_resource]; if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.file_transfer.direction]; time = new DateTime.from_unix_utc(row[db.file_transfer.time]); local_time = new DateTime.from_unix_utc(row[db.file_transfer.local_time]); encryption = (Encryption) row[db.file_transfer.encryption]; file_name = row[db.file_transfer.file_name]; path = row[db.file_transfer.path]; mime_type = row[db.file_transfer.mime_type]; size = row[db.file_transfer.size]; state = (State) row[db.file_transfer.state]; provider = row[db.file_transfer.provider]; info = row[db.file_transfer.info]; notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.file_transfer.insert() .value(db.file_transfer.account_id, account.id) .value(db.file_transfer.counterpart_id, db.get_jid_id(counterpart)) .value(db.file_transfer.counterpart_resource, counterpart.resourcepart) .value(db.file_transfer.our_resource, ourpart.resourcepart) .value(db.file_transfer.direction, direction) .value(db.file_transfer.time, (long) time.to_unix()) .value(db.file_transfer.local_time, (long) local_time.to_unix()) .value(db.file_transfer.encryption, encryption) .value(db.file_transfer.file_name, file_name) .value(db.file_transfer.size, size) .value(db.file_transfer.state, state) .value(db.file_transfer.provider, provider) .value(db.file_transfer.info, info); if (file_name != null) builder.value(db.file_transfer.file_name, file_name); if (path != null) builder.value(db.file_transfer.path, path); if (mime_type != null) builder.value(db.file_transfer.mime_type, mime_type); id = (int) builder.perform(); notify.connect(on_update); } public File get_file() { return File.new_for_path(Path.build_filename(Dino.get_storage_dir(), "files", path)); } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.file_transfer.update().with(db.file_transfer.id, "=", id); switch (sp.name) { case "counterpart": update_builder.set(db.file_transfer.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.file_transfer.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.file_transfer.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.file_transfer.direction, direction); break; case "time": update_builder.set(db.file_transfer.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.file_transfer.local_time, (long) local_time.to_unix()); break; case "encryption": update_builder.set(db.file_transfer.encryption, encryption); break; case "file-name": update_builder.set(db.file_transfer.file_name, file_name); break; case "path": update_builder.set(db.file_transfer.path, path); break; case "mime-type": update_builder.set(db.file_transfer.mime_type, mime_type); break; case "size": update_builder.set(db.file_transfer.size, size); break; case "state": if (state == State.IN_PROGRESS) return; update_builder.set(db.file_transfer.state, state); break; case "provider": update_builder.set(db.file_transfer.provider, provider); break; case "info": update_builder.set(db.file_transfer.info, info); break; } update_builder.perform(); } } } dino-0.4.3/libdino/src/entity/message.vala0000644000000000000000000002200414452563620017166 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Entities { public class Message : Object { public const bool DIRECTION_SENT = true; public const bool DIRECTION_RECEIVED = false; public enum Marked { NONE, RECEIVED, READ, ACKNOWLEDGED, UNSENT, WONTSEND, SENDING, SENT, ERROR } public static Marked[] MARKED_RECEIVED = new Marked[] { Marked.READ, Marked.RECEIVED, Marked.ACKNOWLEDGED }; public enum Type { ERROR, CHAT, GROUPCHAT, GROUPCHAT_PM, UNKNOWN; public bool is_muc_semantic() { return this == GROUPCHAT || this == GROUPCHAT_PM; } } public int id { get; set; default = -1; } public Account account { get; set; } public Jid? counterpart { get; set; } public Jid? ourpart { get; set; } public Jid? from { get { return direction == DIRECTION_SENT ? ourpart : counterpart; } } public Jid? to { get { return direction == DIRECTION_SENT ? counterpart : ourpart; } } public bool direction { get; set; } public Jid? real_jid { get; set; } public Type type_ { get; set; default = Type.UNKNOWN; } private string? body_; public string? body { get { return body_; } set { body_ = value != null ? value.make_valid() : null; } } public string? stanza_id { get; set; } public string? server_id { get; set; } public DateTime? time { get; set; } /** UTC **/ public DateTime? local_time { get; set; } public Encryption encryption { get; set; default = Encryption.NONE; } private Marked marked_ = Marked.NONE; public Marked marked { get { return marked_; } set { if (value == Marked.RECEIVED && marked == Marked.READ) return; marked_ = value; } } public string? edit_to = null; public int quoted_item_id = 0; private Gee.List fallbacks = null; private Database? db; public Message(string? body) { this.body = body; } public Message.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.message.id]; account = db.get_account_by_id(row[db.message.account_id]); stanza_id = row[db.message.stanza_id]; server_id = row[db.message.server_id]; type_ = (Message.Type) row[db.message.type_]; counterpart = db.get_jid_by_id(row[db.message.counterpart_id]); string counterpart_resource = row[db.message.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); string our_resource = row[db.message.our_resource]; if (type_ == Type.GROUPCHAT && our_resource != null) { ourpart = counterpart.with_resource(our_resource); } else if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.message.direction]; time = new DateTime.from_unix_utc(row[db.message.time]); local_time = new DateTime.from_unix_utc(row[db.message.local_time]); body = row[db.message.body]; marked = (Message.Marked) row[db.message.marked]; encryption = (Encryption) row[db.message.encryption]; string? real_jid_str = row[db.real_jid.real_jid]; if (real_jid_str != null) real_jid = new Jid(real_jid_str); edit_to = row[db.message_correction.to_stanza_id]; quoted_item_id = row[db.reply.quoted_content_item_id]; notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.message.insert() .value(db.message.account_id, account.id) .value(db.message.counterpart_id, db.get_jid_id(counterpart)) .value(db.message.counterpart_resource, counterpart.resourcepart) .value(db.message.our_resource, ourpart.resourcepart) .value(db.message.direction, direction) .value(db.message.type_, type_) .value(db.message.time, (long) time.to_unix()) .value(db.message.local_time, (long) local_time.to_unix()) .value(db.message.body, body) .value(db.message.encryption, encryption) .value(db.message.marked, marked); if (stanza_id != null) builder.value(db.message.stanza_id, stanza_id); if (server_id != null) builder.value(db.message.server_id, server_id); id = (int) builder.perform(); if (real_jid != null) { db.real_jid.insert() .value(db.real_jid.message_id, id) .value(db.real_jid.real_jid, real_jid.to_string()) .perform(); } notify.connect(on_update); } public Gee.List get_fallbacks() { if (fallbacks != null) return fallbacks; var fallbacks_by_ns = new HashMap>(); foreach (Qlite.Row row in db.body_meta.select().with(db.body_meta.message_id, "=", id)) { if (row[db.body_meta.info_type] != Xep.FallbackIndication.NS_URI) continue; string ns_uri = row[db.body_meta.info]; if (!fallbacks_by_ns.has_key(ns_uri)) { fallbacks_by_ns[ns_uri] = new ArrayList(); } fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char])); } var fallbacks = new ArrayList(); foreach (string ns_uri in fallbacks_by_ns.keys) { fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri].to_array())); } this.fallbacks = fallbacks; return fallbacks; } public void set_fallbacks(Gee.List fallbacks) { this.fallbacks = fallbacks; } public void set_type_string(string type) { switch (type) { case Xmpp.MessageStanza.TYPE_CHAT: type_ = Type.CHAT; break; case Xmpp.MessageStanza.TYPE_GROUPCHAT: type_ = Type.GROUPCHAT; break; } } public new string get_type_string() { switch (type_) { case Type.CHAT: return Xmpp.MessageStanza.TYPE_CHAT; case Type.GROUPCHAT: return Xmpp.MessageStanza.TYPE_GROUPCHAT; default: return Xmpp.MessageStanza.TYPE_NORMAL; } } public bool equals(Message? m) { if (m == null) return false; return equals_func(this, m); } public static bool equals_func(Message m1, Message m2) { if (m1.stanza_id == m2.stanza_id && m1.body == m2.body) { return true; } return false; } public static uint hash_func(Message message) { return message.body.hash(); } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.message.update().with(db.message.id, "=", id); switch (sp.name) { case "stanza-id": update_builder.set(db.message.stanza_id, stanza_id); break; case "server-id": update_builder.set(db.message.server_id, server_id); break; case "counterpart": update_builder.set(db.message.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.message.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.message.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.message.direction, direction); break; case "type-": update_builder.set(db.message.type_, type_); break; case "time": update_builder.set(db.message.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.message.local_time, (long) local_time.to_unix()); break; case "body": update_builder.set(db.message.body, body); break; case "encryption": update_builder.set(db.message.encryption, encryption); break; case "marked": update_builder.set(db.message.marked, marked); break; } update_builder.perform(); if (sp.get_name() == "real-jid") { db.real_jid.upsert() .value(db.real_jid.message_id, id, true) .value(db.real_jid.real_jid, real_jid.to_string()) .perform(); } if (sp.get_name() == "quoted-item-id") { db.reply.upsert() .value(db.reply.message_id, id, true) .value(db.reply.quoted_content_item_id, quoted_item_id) .perform(); } } } } dino-0.4.3/libdino/src/entity/settings.vala0000644000000000000000000000523614452563620017412 0ustar rootrootnamespace Dino.Entities { public class Settings : Object { private Database db; public Settings.from_db(Database db) { this.db = db; send_typing_ = col_to_bool_or_default("send_typing", true); send_marker_ = col_to_bool_or_default("send_marker", true); notifications_ = col_to_bool_or_default("notifications", true); convert_utf8_smileys_ = col_to_bool_or_default("convert_utf8_smileys", true); check_spelling = col_to_bool_or_default("check_spelling", true); } private bool col_to_bool_or_default(string key, bool def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? bool.parse(val) : def; } private bool send_typing_; public bool send_typing { get { return send_typing_; } set { db.settings.upsert() .value(db.settings.key, "send_typing", true) .value(db.settings.value, value.to_string()) .perform(); send_typing_ = value; } } private bool send_marker_; public bool send_marker { get { return send_marker_; } set { db.settings.upsert() .value(db.settings.key, "send_marker", true) .value(db.settings.value, value.to_string()) .perform(); send_marker_ = value; } } private bool notifications_; public bool notifications { get { return notifications_; } set { db.settings.upsert() .value(db.settings.key, "notifications", true) .value(db.settings.value, value.to_string()) .perform(); notifications_ = value; } } private bool convert_utf8_smileys_; public bool convert_utf8_smileys { get { return convert_utf8_smileys_; } set { db.settings.upsert() .value(db.settings.key, "convert_utf8_smileys", true) .value(db.settings.value, value.to_string()) .perform(); convert_utf8_smileys_ = value; } } // There is currently no spell checking for GTK4, thus there is currently no UI for this setting. private bool check_spelling_; public bool check_spelling { get { return check_spelling_; } set { db.settings.upsert() .value(db.settings.key, "check_spelling", true) .value(db.settings.value, value.to_string()) .perform(); check_spelling_ = value; } } } } dino-0.4.3/libdino/src/plugin/0000755000000000000000000000000014452563620014661 5ustar rootrootdino-0.4.3/libdino/src/plugin/interfaces.vala0000644000000000000000000001624614452563620017662 0ustar rootrootusing Dino.Entities; using Xmpp; namespace Dino.Plugins { public enum Priority { LOWEST, LOWER, DEFAULT, HIGHER, HIGHEST } public enum WidgetType { GTK3, GTK4 } public interface RootInterface : Object { public abstract void registered(Dino.Application app); public abstract void shutdown(); } public interface EncryptionListEntry : Object { public abstract Entities.Encryption encryption { get; } public abstract string name { get; } public abstract void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus callback); public abstract Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item); public abstract string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item); } public interface CallEncryptionEntry : Object { public abstract CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption); } public interface CallEncryptionWidget : Object { public abstract string? get_title(); public abstract bool show_keys(); public abstract string? get_icon_name(); } public abstract class AccountSettingsEntry : Object { public abstract string id { get; } public virtual Priority priority { get { return Priority.DEFAULT; } } public abstract string name { get; } public virtual int16 label_top_padding { get { return -1; } } public abstract signal void activated(); public abstract void deactivate(); public abstract void set_account(Account account); public abstract Object? get_widget(WidgetType type); } public interface ContactDetailsProvider : Object { public abstract string id { get; } public abstract void populate(Conversation conversation, ContactDetails contact_details, WidgetType type); } public class ContactDetails : Object { public signal void save(); public signal void add(string category, string label, string? desc, Object widget); } public interface TextCommand : Object { public abstract string cmd { get; } public abstract string? handle_command(string? text, Entities.Conversation? conversation); } public interface ConversationTitlebarEntry : Object { public abstract string id { get; } public abstract double order { get; } public abstract Object? get_widget(WidgetType type); public abstract void set_conversation(Conversation conversation); public abstract void unset_conversation(); } public abstract interface ConversationItemPopulator : Object { public abstract string id { get; } public abstract void init(Conversation conversation, ConversationItemCollection summary, WidgetType type); public abstract void close(Conversation conversation); } public abstract interface ConversationAdditionPopulator : ConversationItemPopulator { public virtual void populate_timespan(Conversation conversation, DateTime from, DateTime to) { } } public abstract interface VideoCallPlugin : Object { public abstract bool supports(string? media); // Video widget public abstract VideoCallWidget? create_widget(WidgetType type); // Devices public signal void devices_changed(string media, bool incoming); public abstract Gee.List get_devices(string media, bool incoming); public abstract MediaDevice? get_preferred_device(string media, bool incoming); public abstract MediaDevice? get_device(Xmpp.Xep.JingleRtp.Stream? stream, bool incoming); public abstract void set_pause(Xmpp.Xep.JingleRtp.Stream? stream, bool pause); public abstract void set_device(Xmpp.Xep.JingleRtp.Stream? stream, MediaDevice? device); public abstract void dump_dot(); } public abstract interface VideoCallWidget : Object { public signal void resolution_changed(uint width, uint height); public abstract void display_stream(Xmpp.Xep.JingleRtp.Stream? stream, Jid jid); public abstract void display_device(MediaDevice device); public abstract void detach(); } public abstract interface MediaDevice : Object { public abstract string id { owned get; } public abstract string display_name { owned get; } public abstract string? detail_name { owned get; } public abstract string? media { owned get; } public abstract bool incoming { get; } } public abstract interface NotificationPopulator : Object { public abstract string id { get; } public abstract void init(Conversation conversation, NotificationCollection summary, WidgetType type); public abstract void close(Conversation conversation); } public abstract class MetaConversationItem : Object { public virtual string populator_id { get; set; } public virtual Jid? jid { get; set; default=null; } public virtual DateTime time { get; set; default = new DateTime.now_utc(); } public virtual int secondary_sort_indicator { get; set; } public virtual Encryption encryption { get; set; default = Encryption.NONE; } public virtual Entities.Message.Marked mark { get; set; default = Entities.Message.Marked.NONE; } public bool can_merge { get; set; default=false; } public bool requires_avatar { get; set; default=false; } public bool requires_header { get; set; default=false; } public bool in_edit_mode { get; set; default=false; } public abstract Object? get_widget(ConversationItemWidgetInterface outer, WidgetType type); public abstract Gee.List? get_item_actions(WidgetType type); } public interface ConversationItemWidgetInterface: Object { public abstract void set_widget(Object object, WidgetType type, int priority); } public delegate void MessageActionEvoked(Variant? variant); public class MessageAction : Object { public string name; public bool sensitive = true; public string icon_name; public string? tooltip; public Object? popover; public MessageActionEvoked? callback; } public abstract class MetaConversationNotification : Object { public abstract Object? get_widget(WidgetType type); } public interface ConversationItemCollection : Object { public signal void inserted_item(MetaConversationItem item); public signal void removed_item(MetaConversationItem item); public abstract void insert_item(MetaConversationItem item); public abstract void remove_item(MetaConversationItem item); } public interface NotificationCollection : Object { public signal void add_meta_notification(MetaConversationNotification item); public signal void remove_meta_notification(MetaConversationNotification item); } public delegate void SetInputFieldStatus(InputFieldStatus field_status); public class InputFieldStatus : Object { public enum MessageType { NONE, INFO, WARNING, ERROR } public enum InputState { NORMAL, DISABLED, NO_SEND } public string? message; public MessageType message_type; public InputState input_state; public bool contains_markup; public InputFieldStatus(string? message, MessageType message_type, InputState input_state, bool contains_markup = false) { this.message = message; this.message_type = message_type; this.input_state = input_state; this.contains_markup = contains_markup; } } } dino-0.4.3/libdino/src/plugin/loader.vala0000644000000000000000000000532214452563620016776 0ustar rootrootusing Gee; namespace Dino.Plugins { private class Info : Object { public Module module; public Type gtype; public Info(Type type, owned Module module) { this.module = (owned) module; this.gtype = type; } } public class Loader : Object { [CCode (has_target = false)] private delegate Type RegisterPluginFunction(Module module); private Application app; private string[] search_paths; private RootInterface[] plugins = new RootInterface[0]; private Info[] infos = new Info[0]; public Loader(Application app) { this.app = app; this.search_paths = app.search_path_generator.get_plugin_paths(); } public void load_all() throws Error { if (Module.supported() == false) { throw new Error(-1, 0, "Plugins are not supported"); } HashSet plugin_names = new HashSet(); foreach (string path in search_paths) { try { Dir dir = Dir.open(path, 0); string? file = null; while ((file = dir.read_name()) != null) { if (file.has_suffix(Module.SUFFIX)) plugin_names.add(file); } } catch (Error e) { // Ignore this folder } } foreach (string plugin in plugin_names) { load(plugin); } } public RootInterface load(string name) throws Error { if (Module.supported() == false) { throw new Error(-1, 0, "Plugins are not supported"); } Module module = null; string path = ""; foreach (string prefix in search_paths) { path = Path.build_filename(prefix, name); module = Module.open(path, ModuleFlags.BIND_LAZY); if (module != null) break; } if (module == null) { throw new Error(-1, 1, "%s", Module.error().replace(path, name)); } void* function; module.symbol("register_plugin", out function); if (function == null) { throw new Error(-1, 2, "register_plugin () not found"); } RegisterPluginFunction register_plugin = (RegisterPluginFunction) function; Type type = register_plugin(module); if (type.is_a(typeof(RootInterface)) == false) { throw new Error(-1, 3, "Unexpected type"); } Info info = new Plugins.Info(type, (owned) module); infos += info; RootInterface plugin = (RootInterface) Object.new (type); plugins += plugin; plugin.registered(app); return plugin; } public void shutdown() { foreach (RootInterface p in plugins) { p.shutdown(); } } } } dino-0.4.3/libdino/src/plugin/registry.vala0000644000000000000000000000736514452563620017411 0ustar rootrootusing Gee; namespace Dino.Plugins { public class Registry { internal HashMap encryption_list_entries = new HashMap(); internal HashMap call_encryption_entries = new HashMap(); internal ArrayList account_settings_entries = new ArrayList(); internal ArrayList contact_details_entries = new ArrayList(); internal Map text_commands = new HashMap(); internal Gee.List conversation_addition_populators = new ArrayList(); internal Gee.List notification_populators = new ArrayList(); internal Gee.Collection conversation_titlebar_entries = new Gee.TreeSet((a, b) => { return (int)(a.order - b.order); }); public VideoCallPlugin? video_call_plugin; public bool register_encryption_list_entry(EncryptionListEntry entry) { lock(encryption_list_entries) { if (encryption_list_entries.has_key(entry.encryption)) return false; encryption_list_entries[entry.encryption] = entry; return true; } } public bool register_call_entryption_entry(string ns, CallEncryptionEntry entry) { lock (call_encryption_entries) { call_encryption_entries[ns] = entry; } return true; } public bool register_account_settings_entry(AccountSettingsEntry entry) { lock(account_settings_entries) { foreach(var e in account_settings_entries) { if (e.id == entry.id) return false; } account_settings_entries.add(entry); // TODO: Order by priority account_settings_entries.sort((a,b) => b.name.collate(a.name)); return true; } } public bool register_contact_details_entry(ContactDetailsProvider entry) { lock(contact_details_entries) { foreach(ContactDetailsProvider e in contact_details_entries) { if (e.id == entry.id) return false; } contact_details_entries.add(entry); return true; } } public bool register_text_command(TextCommand cmd) { lock(text_commands) { if (text_commands.has_key(cmd.cmd)) return false; text_commands[cmd.cmd] = cmd; return true; } } public bool register_contact_titlebar_entry(ConversationTitlebarEntry entry) { lock(conversation_titlebar_entries) { foreach(ConversationTitlebarEntry e in conversation_titlebar_entries) { if (e.id == entry.id) return false; } conversation_titlebar_entries.add(entry); return true; } } public bool register_conversation_addition_populator(ConversationAdditionPopulator populator) { lock (conversation_addition_populators) { foreach(ConversationItemPopulator p in conversation_addition_populators) { if (p.id == populator.id) return false; } conversation_addition_populators.add(populator); return true; } } public bool register_notification_populator(NotificationPopulator populator) { lock (notification_populators) { foreach(NotificationPopulator p in notification_populators) { if (p.id == populator.id) return false; } notification_populators.add(populator); return true; } } } } dino-0.4.3/libdino/src/service/0000755000000000000000000000000014452563620015023 5ustar rootrootdino-0.4.3/libdino/src/service/avatar_manager.vala0000644000000000000000000002345114452563620020645 0ustar rootrootusing Gdk; using Gee; using Qlite; using Xmpp; using Dino.Entities; namespace Dino { public class AvatarManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("avatar_manager"); public string id { get { return IDENTITY.id; } } public signal void received_avatar(Jid jid, Account account); private enum Source { USER_AVATARS, VCARD } private StreamInteractor stream_interactor; private Database db; private string folder = null; private HashMap user_avatars = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap vcard_avatars = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap cached_pixbuf = new HashMap(); private HashMap> pending_pixbuf = new HashMap>(); private const int MAX_PIXEL = 192; public static void start(StreamInteractor stream_interactor, Database db) { AvatarManager m = new AvatarManager(stream_interactor, db); stream_interactor.add_module(m); } private AvatarManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.folder = Path.build_filename(Dino.get_storage_dir(), "avatars"); DirUtils.create_with_parents(this.folder, 0700); stream_interactor.account_added.connect(on_account_added); stream_interactor.module_manager.initialize_account_modules.connect((_, modules) => { modules.add(new Xep.UserAvatars.Module()); modules.add(new Xep.VCard.Module()); }); } private string? get_avatar_hash(Account account, Jid jid_) { Jid jid = jid_; if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { jid = jid_.bare_jid; } if (user_avatars.has_key(jid)) { return user_avatars[jid]; } else if (vcard_avatars.has_key(jid)) { return vcard_avatars[jid]; } else { return null; } } public bool has_avatar_cached(Account account, Jid jid) { string? hash = get_avatar_hash(account, jid); return hash != null && cached_pixbuf.has_key(hash); } public bool has_avatar(Account account, Jid jid) { return get_avatar_hash(account, jid) != null; } public Pixbuf? get_cached_avatar(Account account, Jid jid_) { string? hash = get_avatar_hash(account, jid_); if (hash == null) return null; if (cached_pixbuf.has_key(hash)) return cached_pixbuf[hash]; return null; } public async Pixbuf? get_avatar(Account account, Jid jid_) { Jid jid = jid_; if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { jid = jid_.bare_jid; } int source = -1; string? hash = null; if (user_avatars.has_key(jid)) { hash = user_avatars[jid]; source = 1; } else if (vcard_avatars.has_key(jid)) { hash = vcard_avatars[jid]; source = 2; } if (hash == null) return null; if (cached_pixbuf.has_key(hash)) { return cached_pixbuf[hash]; } XmppStream? stream = stream_interactor.get_stream(account); if (stream == null || !stream.negotiation_complete) return null; if (pending_pixbuf.has_key(hash)) { pending_pixbuf[hash].add(new SourceFuncWrapper(get_avatar.callback)); yield; return cached_pixbuf[hash]; } pending_pixbuf[hash] = new ArrayList(); Pixbuf? image = yield get_image(hash); if (image != null) { cached_pixbuf[hash] = image; } else { Bytes? bytes = null; if (source == 1) { bytes = yield Xmpp.Xep.UserAvatars.fetch_image(stream, jid, hash); } else if (source == 2) { bytes = yield Xmpp.Xep.VCard.fetch_image(stream, jid, hash); if (bytes == null && jid.is_bare()) { db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(jid)).perform(); } } if (bytes != null) { store_image(hash, bytes); image = yield get_image(hash); } cached_pixbuf[hash] = image; } foreach (SourceFuncWrapper sfw in pending_pixbuf[hash]) { sfw.sfun(); } return image; } public void publish(Account account, string file) { try { Pixbuf pixbuf = new Pixbuf.from_file(file); if (pixbuf.width >= pixbuf.height && pixbuf.width > MAX_PIXEL) { int dest_height = (int) ((float) MAX_PIXEL / pixbuf.width * pixbuf.height); pixbuf = pixbuf.scale_simple(MAX_PIXEL, dest_height, InterpType.BILINEAR); } else if (pixbuf.height > pixbuf.width && pixbuf.width > MAX_PIXEL) { int dest_width = (int) ((float) MAX_PIXEL / pixbuf.height * pixbuf.width); pixbuf = pixbuf.scale_simple(dest_width, MAX_PIXEL, InterpType.BILINEAR); } uint8[] buffer; pixbuf.save_to_buffer(out buffer, "png"); XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { Xmpp.Xep.UserAvatars.publish_png(stream, buffer, pixbuf.width, pixbuf.height); } } catch (Error e) { warning(e.message); } } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) => on_user_avatar_received.begin(account, jid, id) ); stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) => on_vcard_avatar_received.begin(account, jid, id) ); foreach (var entry in get_avatar_hashes(account, Source.USER_AVATARS).entries) { user_avatars[entry.key] = entry.value; } foreach (var entry in get_avatar_hashes(account, Source.VCARD).entries) { // FIXME: remove. temporary to remove falsely saved avatars. if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(entry.key, account)) { db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(entry.key)).perform(); continue; } vcard_avatars[entry.key] = entry.value; } } private async void on_user_avatar_received(Account account, Jid jid_, string id) { Jid jid = jid_.bare_jid; if (!user_avatars.has_key(jid) || user_avatars[jid] != id) { user_avatars[jid] = id; set_avatar_hash(account, jid, id, Source.USER_AVATARS); } received_avatar(jid, account); } private async void on_vcard_avatar_received(Account account, Jid jid_, string id) { bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(jid_.bare_jid, account); Jid jid = is_gc ? jid_ : jid_.bare_jid; if (!vcard_avatars.has_key(jid) || vcard_avatars[jid] != id) { vcard_avatars[jid] = id; if (jid.is_bare()) { // don't save MUC occupant avatars set_avatar_hash(account, jid, id, Source.VCARD); } } received_avatar(jid, account); } public void set_avatar_hash(Account account, Jid jid, string hash, int type) { db.avatar.insert() .value(db.avatar.jid_id, db.get_jid_id(jid)) .value(db.avatar.account_id, account.id) .value(db.avatar.hash, hash) .value(db.avatar.type_, type) .perform(); } public HashMap get_avatar_hashes(Account account, int type) { HashMap ret = new HashMap(Jid.hash_func, Jid.equals_func); foreach (Row row in db.avatar.select({db.avatar.jid_id, db.avatar.hash}) .with(db.avatar.type_, "=", type) .with(db.avatar.account_id, "=", account.id)) { ret[db.get_jid_by_id(row[db.avatar.jid_id])] = row[db.avatar.hash]; } return ret; } public void store_image(string id, Bytes data) { File file = File.new_for_path(Path.build_filename(folder, id)); try { if (file.query_exists()) file.delete(); //TODO y? DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION)); fos.write_bytes_async.begin(data); } catch (Error e) { // Ignore: we failed in storing, so we refuse to display later... } } public bool has_image(string id) { File file = File.new_for_path(Path.build_filename(folder, id)); return file.query_exists(); } public async Pixbuf? get_image(string id) { try { File file = File.new_for_path(Path.build_filename(folder, id)); FileInputStream stream = yield file.read_async(Priority.LOW); uint8 fbuf[1024]; size_t size; Checksum checksum = new Checksum (ChecksumType.SHA1); while ((size = yield stream.read_async(fbuf, Priority.LOW)) > 0) { checksum.update(fbuf, size); } if (checksum.get_string() != id) { FileUtils.remove(file.get_path()); } stream.seek(0, SeekType.SET); return yield new Pixbuf.from_stream_async(stream, null); } catch (Error e) { return null; } } } } dino-0.4.3/libdino/src/service/blocking_manager.vala0000644000000000000000000000312214452563620021150 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class BlockingManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("blocking_manager"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; public static void start(StreamInteractor stream_interactor) { BlockingManager m = new BlockingManager(stream_interactor); stream_interactor.add_module(m); } private BlockingManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool is_blocked(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_blocked(stream, jid.to_string()); } public void block(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).block(stream, { jid.to_string() }); } public void unblock(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).unblock(stream, { jid.to_string() }); } public bool is_supported(Account account) { XmppStream stream = stream_interactor.get_stream(account); return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_supported(stream); } } } dino-0.4.3/libdino/src/service/call_peer_state.vala0000644000000000000000000005152314452563620021024 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; public class Dino.PeerState : Object { public signal void stream_created(string media); public signal void counterpart_sends_video_updated(bool mute); public signal void info_received(Xep.JingleRtp.CallSessionInfo session_info); public signal void connection_ready(); public signal void session_terminated(bool we_terminated, string? reason_name, string? reason_text); public signal void encryption_updated(Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same); public StreamInteractor stream_interactor; public CallState call_state; public Calls calls; public Call call; public Jid jid; public Xep.Jingle.Session session; public string sid; public string internal_id = Xmpp.random_uuid(); public Xep.JingleRtp.Parameters? audio_content_parameter = null; public Xep.JingleRtp.Parameters? video_content_parameter = null; public Xep.Jingle.Content? audio_content = null; public Xep.Jingle.Content? video_content = null; public Xep.Jingle.ContentEncryption? video_encryption = null; public Xep.Jingle.ContentEncryption? audio_encryption = null; public bool encryption_keys_same = false; public HashMap? video_encryptions = null; public HashMap? audio_encryptions = null; public bool first_peer = false; public bool waiting_for_inbound_muji_connection = false; public Xep.Muji.GroupCall? group_call { get; set; } public bool counterpart_sends_video = false; public bool we_should_send_audio { get; set; default=false; } public bool we_should_send_video { get; set; default=false; } public PeerState(Jid jid, Call call, CallState call_state, StreamInteractor stream_interactor) { this.jid = jid; this.call = call; this.call_state = call_state; this.stream_interactor = stream_interactor; this.calls = stream_interactor.get_module(Calls.IDENTITY); Xep.JingleRtp.Module jinglertp_module = stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY); if (jinglertp_module == null) return; var session_info_type = jinglertp_module.session_info_type; session_info_type.mute_update_received.connect((session,mute, name) => { if (this.sid != session.sid) return; foreach (Xep.Jingle.Content content in session.contents) { if (name == null || content.content_name == name) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { on_counterpart_mute_update(mute, rtp_content_parameter.media); } } } }); session_info_type.info_received.connect((session, session_info) => { if (this.sid != session.sid) return; info_received(session_info); }); } public async void initiate_call(Jid counterpart) { Gee.List call_resources = yield calls.get_call_resources(call.account, counterpart); bool do_jmi = false; Jid? jid_for_direct = null; if (yield calls.contains_jmi_resources(call.account, call_resources)) { do_jmi = true; } else if (!call_resources.is_empty) { jid_for_direct = call_resources[0]; } else if (calls.has_jmi_resources(jid)) { do_jmi = true; } sid = Xmpp.random_uuid(); if (do_jmi) { XmppStream? stream = stream_interactor.get_stream(call.account); var descriptions = new ArrayList(); descriptions.add(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "audio")); if (we_should_send_video) { descriptions.add(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "video")); } stream.get_module(Xmpp.Xep.JingleMessageInitiation.Module.IDENTITY).send_session_propose_to_peer(stream, jid, sid, descriptions); // Uncomment this use CIM instead of JMI // call_state.cim_call_id = sid; // stream.get_module(Xmpp.Xep.CallInvites.Module.IDENTITY).send_jingle_propose(stream, call_state.cim_call_id, jid, sid, we_should_send_video); } else if (jid_for_direct != null) { yield call_resource(jid_for_direct); } } public async void call_resource(Jid full_jid) { if (!call_state.accepted) { warning("Tried to call resource in an unaccepted call?!"); return; } XmppStream? stream = stream_interactor.get_stream(call.account); if (stream == null) return; if (sid == null) sid = Xmpp.random_uuid(); Xep.Jingle.Session session = yield stream.get_module(Xep.JingleRtp.Module.IDENTITY).start_call(stream, full_jid, we_should_send_video, sid, group_call != null ? group_call.muc_jid : null); set_session(session); } public void accept() { if (!call_state.accepted) { critical("Tried to accept peer in unaccepted call?! Something's fishy. Abort."); return; } if (session != null) { foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null && rtp_content_parameter.media == "video") { // We didn't accept video but our peer wants to negotiate that content if (!we_should_send_video && session.senders_include_us(content.senders)) { if (session.senders_include_counterpart(content.senders)) { // If our peer wants to send, let them content.modify(session.we_initiated ? Xep.Jingle.Senders.RESPONDER : Xep.Jingle.Senders.INITIATOR); } else { // If only we're supposed to send, reject content.reject(); continue; } } } content.accept(); } } else { // Only a JMI so far XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_accept_to_self(stream, sid); stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_proceed_to_peer(stream, jid, sid); } } public void reject() { if (session != null) { foreach (Xep.Jingle.Content content in session.contents) { content.reject(); } } else { // Only a JMI so far XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_reject_to_peer(stream, jid, sid); stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_reject_to_self(stream, sid); } } public void end(string terminate_reason, string? reason_text = null) { switch (terminate_reason) { case Xep.Jingle.ReasonElement.SUCCESS: if (session != null) { session.terminate(terminate_reason, reason_text, "success"); } break; case Xep.Jingle.ReasonElement.CANCEL: if (session != null) { session.terminate(terminate_reason, reason_text, "cancel"); } else if (group_call != null) { // We don't have to do anything (?) } else { // Only a JMI so far XmppStream? stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_retract_to_peer(stream, jid, sid); } break; } } internal void mute_own_audio(bool mute) { // Call isn't fully established yet. Audio will be muted once the stream is created. if (session == null || audio_content_parameter == null || audio_content_parameter.stream == null) return; Xep.JingleRtp.Stream stream = audio_content_parameter.stream; // Inform our counterpart that we (un)muted our audio stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY).session_info_type.send_mute(session, mute, "audio"); // Start/Stop sending audio data Application.get_default().plugin_registry.video_call_plugin.set_pause(stream, mute); } internal void mute_own_video(bool mute) { if (session == null) { // Call hasn't been established yet return; } Xep.JingleRtp.Module rtp_module = stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY); if (video_content_parameter != null && video_content_parameter.stream != null && session.senders_include_us(video_content.senders)) { // A video content already exists // Start/Stop sending video data Xep.JingleRtp.Stream stream = video_content_parameter.stream; if (stream != null) { Application.get_default().plugin_registry.video_call_plugin.set_pause(stream, mute); } // Inform our counterpart that we started/stopped our video rtp_module.session_info_type.send_mute(session, mute, "video"); } else if (!mute) { // Add a new video content XmppStream stream = stream_interactor.get_stream(call.account); rtp_module.add_outgoing_video_content.begin(stream, session, group_call != null ? group_call.muc_jid : null, (_, res) => { if (video_content_parameter == null) { Xep.Jingle.Content content = rtp_module.add_outgoing_video_content.end(res); Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { connect_content_signals(content, rtp_content_parameter); } } }); } // If video_content_parameter == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created. } public Xep.JingleRtp.Stream? get_video_stream() { if (video_content_parameter != null) { return video_content_parameter.stream; } return null; } public Xep.JingleRtp.Stream? get_audio_stream() { if (audio_content_parameter != null) { return audio_content_parameter.stream; } return null; } internal void set_session(Xep.Jingle.Session session) { this.session = session; this.sid = session.sid; session.terminated.connect((stream, we_terminated, reason_name, reason_text) => session_terminated(we_terminated, reason_name, reason_text) ); session.additional_content_add_incoming.connect((stream, content) => on_incoming_content_add(stream, content.session, content) ); foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) continue; connect_content_signals(content, rtp_content_parameter); } } public PeerInfo get_info() { var ret = new PeerInfo(); if (audio_content != null || audio_content_parameter != null) { ret.audio = get_content_info(audio_content, audio_content_parameter); } if (video_content != null || video_content_parameter != null) { ret.video = get_content_info(video_content, video_content_parameter); } return ret; } private PeerContentInfo get_content_info(Xep.Jingle.Content? content, Xep.JingleRtp.Parameters? parameter) { PeerContentInfo ret = new PeerContentInfo(); if (parameter != null) { ret.rtcp_ready = parameter.rtcp_ready; ret.rtp_ready = parameter.rtp_ready; if (parameter.agreed_payload_type != null) { ret.codec = parameter.agreed_payload_type.name; ret.clockrate = parameter.agreed_payload_type.clockrate; } if (parameter.stream != null && parameter.stream.remb_enabled) { ret.target_receive_bytes = parameter.stream.target_receive_bitrate; ret.target_send_bytes = parameter.stream.target_send_bitrate; } } if (content != null) { Xmpp.Xep.Jingle.ComponentConnection? component0 = content.get_transport_connection(1); if (component0 != null) { ret.bytes_received = component0.bytes_received; ret.bytes_sent = component0.bytes_sent; } } return ret; } private void connect_content_signals(Xep.Jingle.Content content, Xep.JingleRtp.Parameters rtp_content_parameter) { if (rtp_content_parameter.media == "audio") { audio_content = content; audio_content_parameter = rtp_content_parameter; } else if (rtp_content_parameter.media == "video") { video_content = content; video_content_parameter = rtp_content_parameter; } debug(@"[%s] %s connecting content signals %s", call.account.bare_jid.to_string(), jid.to_string(), rtp_content_parameter.media); rtp_content_parameter.stream_created.connect((stream) => on_stream_created(rtp_content_parameter.media, stream)); rtp_content_parameter.connection_ready.connect((status) => { Idle.add(() => { on_connection_ready(content, rtp_content_parameter.media); return false; }); }); content.senders_modify_incoming.connect((content, proposed_senders) => { if (content.session.senders_include_us(content.senders) != content.session.senders_include_us(proposed_senders)) { warning("counterpart set us to (not)sending %s. ignoring", content.content_name); return; } if (!content.session.senders_include_counterpart(content.senders) && content.session.senders_include_counterpart(proposed_senders)) { // Counterpart wants to start sending. Ok. content.accept_content_modify(proposed_senders); on_counterpart_mute_update(false, "video"); } }); } private void on_incoming_content_add(XmppStream stream, Xep.Jingle.Session session, Xep.Jingle.Content content) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) { content.reject(); return; } // Our peer shouldn't tell us to start sending, that's for us to initiate if (session.senders_include_us(content.senders)) { if (session.senders_include_counterpart(content.senders)) { // If our peer wants to send, let them content.modify(session.we_initiated ? Xep.Jingle.Senders.RESPONDER : Xep.Jingle.Senders.INITIATOR); } else { // If only we're supposed to send, reject content.reject(); } } connect_content_signals(content, rtp_content_parameter); content.accept(); } private void on_stream_created(string media, Xep.JingleRtp.Stream stream) { if (media == "video" && stream.receiving) { counterpart_sends_video = true; video_content_parameter.connection_ready.connect((status) => { Idle.add(() => { counterpart_sends_video_updated(false); return false; }); }); } // Outgoing audio/video might have been muted in the meanwhile. if (media == "video" && !we_should_send_video) { mute_own_video(true); } else if (media == "audio" && !we_should_send_audio) { mute_own_audio(true); } stream_created(media); } private void on_counterpart_mute_update(bool mute, string? media) { if (!call.equals(call)) return; if (media == "video") { counterpart_sends_video = !mute; debug(@"[%s] %s video muted %s", call.account.bare_jid.to_string(), jid.to_string(), mute.to_string()); counterpart_sends_video_updated(mute); } } private void on_connection_ready(Xep.Jingle.Content content, string media) { debug("[%s] %s on_connection_ready", call.account.bare_jid.to_string(), jid.to_string()); connection_ready(); if (call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { call.state = Call.State.IN_PROGRESS; } if (media == "audio") { audio_encryptions = content.encryptions; } else if (media == "video") { video_encryptions = content.encryptions; } if ((audio_encryptions != null && audio_encryptions.is_empty) || (video_encryptions != null && video_encryptions.is_empty)) { call.encryption = Encryption.NONE; encryption_updated(null, null, true); return; } HashMap encryptions = audio_encryptions ?? video_encryptions; Xep.Jingle.ContentEncryption? omemo_encryption = null, dtls_encryption = null, srtp_encryption = null; foreach (string encr_name in encryptions.keys) { if (video_encryptions != null && !video_encryptions.has_key(encr_name)) continue; var encryption = encryptions[encr_name]; if (encryption.encryption_ns == "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification") { omemo_encryption = encryption; } else if (encryption.encryption_ns == Xep.JingleIceUdp.DTLS_NS_URI) { dtls_encryption = encryption; } else if (encryption.encryption_name == "SRTP") { srtp_encryption = encryption; } } if (omemo_encryption != null && dtls_encryption != null) { call.encryption = Encryption.OMEMO; omemo_encryption.peer_key = dtls_encryption.peer_key; omemo_encryption.our_key = dtls_encryption.our_key; audio_encryption = omemo_encryption; encryption_keys_same = true; video_encryption = video_encryptions != null ? video_encryptions["http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"] : null; } else if (dtls_encryption != null) { call.encryption = Encryption.DTLS_SRTP; audio_encryption = dtls_encryption; video_encryption = video_encryptions != null ? video_encryptions[Xep.JingleIceUdp.DTLS_NS_URI] : null; encryption_keys_same = true; if (video_encryption != null && dtls_encryption.peer_key.length == video_encryption.peer_key.length) { for (int i = 0; i < dtls_encryption.peer_key.length; i++) { if (dtls_encryption.peer_key[i] != video_encryption.peer_key[i]) { encryption_keys_same = false; break; } } } } else if (srtp_encryption != null) { call.encryption = Encryption.SRTP; audio_encryption = srtp_encryption; video_encryption = video_encryptions != null ? video_encryptions["SRTP"] : null; encryption_keys_same = false; } else { call.encryption = Encryption.NONE; encryption_keys_same = true; } encryption_updated(audio_encryption, video_encryption, encryption_keys_same); } } public class Dino.PeerContentInfo { public bool rtp_ready { get; set; } public bool rtcp_ready { get; set; } public ulong? bytes_sent { get; set; default=0; } public ulong? bytes_received { get; set; default=0; } public string? codec { get; set; } public uint32 clockrate { get; set; } public uint target_receive_bytes { get; set; default=-1; } public uint target_send_bytes { get; set; default=-1; } } public class Dino.PeerInfo { public PeerContentInfo? audio = null; public PeerContentInfo? video = null; }dino-0.4.3/libdino/src/service/call_state.vala0000644000000000000000000005012114452563620020002 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; public class Dino.CallState : Object { public signal void terminated(Jid who_terminated, string? reason_name, string? reason_text); public signal void peer_joined(Jid jid, PeerState peer_state); public signal void peer_left(Jid jid, PeerState peer_state, string? reason_name, string? reason_text); public StreamInteractor stream_interactor; public Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; public Call call; public Jid? parent_muc { get; set; } public Jid? invited_to_group_call = null; public bool accepted { get; private set; default=false; } public bool use_cim = false; public string? cim_call_id = null; public Jid? cim_counterpart = null; public string cim_message_type { get; set; default=Xmpp.MessageStanza.TYPE_CHAT; } public Xep.Muji.GroupCall? group_call { get; set; } public bool we_should_send_audio { get; set; default=false; } public bool we_should_send_video { get; set; default=false; } public HashMap peers = new HashMap(Jid.hash_func, Jid.equals_func); private Plugins.MediaDevice selected_microphone_device; private Plugins.MediaDevice selected_speaker_device; private Plugins.MediaDevice selected_video_device; public CallState(Call call, StreamInteractor stream_interactor) { this.call = call; this.stream_interactor = stream_interactor; if (call.direction == Call.DIRECTION_OUTGOING && call.state != Call.State.OTHER_DEVICE) { accepted = true; Timeout.add_seconds(30, () => { if (this == null) return false; // TODO enough? if (call.state == Call.State.ESTABLISHING) { call.state = Call.State.MISSED; terminated(call.account.bare_jid, null, null); } return false; }); } } internal async void initiate_groupchat_call(Jid muc) { parent_muc = muc; cim_message_type = MessageStanza.TYPE_GROUPCHAT; if (this.group_call == null) yield convert_into_group_call(); if (this.group_call == null) return; // The user might have retracted the call in the meanwhile if (this.call.state != Call.State.RINGING) return; XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; Gee.List occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(muc, call.account); foreach (Jid occupant in occupants) { Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, call.account); if (real_jid == null) continue; debug(@"Adding MUC member as MUJI MUC owner %s", real_jid.bare_jid.to_string()); yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, real_jid.bare_jid, null, "owner"); } stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_propose(stream, cim_call_id, muc, group_call.muc_jid, we_should_send_video, cim_message_type); } internal PeerState set_first_peer(Jid peer) { var peer_state = new PeerState(peer, call, this, stream_interactor); peer_state.first_peer = true; add_peer(peer_state); return peer_state; } internal void add_peer(PeerState peer) { call.add_peer(peer.jid.bare_jid); connect_peer_signals(peer); peer_joined(peer.jid, peer); } internal void on_peer_stream_created(PeerState peer, string media) { if (media == "audio") { call_plugin.set_device(peer.get_audio_stream(), get_microphone_device()); call_plugin.set_device(peer.get_audio_stream(), get_speaker_device()); } else if (media == "video") { call_plugin.set_device(peer.get_video_stream(), get_video_device()); } } public void accept() { accepted = true; call.state = Call.State.ESTABLISHING; if (use_cim) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; StanzaNode? inner_node = null; if (group_call != null) { inner_node = new StanzaNode.build("muji", Xep.Muji.NS_URI).add_self_xmlns() .put_attribute("room", group_call.muc_jid.to_string()); } else if (peers.size == 1) { foreach (PeerState peer in peers.values) { inner_node = new StanzaNode.build("jingle", Xep.CallInvites.NS_URI) .put_attribute("sid", peer.sid); } } stream.get_module(Xep.CallInvites.Module.IDENTITY).send_accept(stream, cim_counterpart, cim_call_id, inner_node, cim_message_type); } else { foreach (PeerState peer in peers.values) { peer.accept(); } } if (invited_to_group_call != null) { join_group_call.begin(invited_to_group_call); } } public void reject() { call.state = Call.State.DECLINED; if (use_cim) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.CallInvites.Module.IDENTITY).send_reject(stream, cim_counterpart, cim_call_id, cim_message_type); } var peers_cpy = new ArrayList(); peers_cpy.add_all(peers.values); foreach (PeerState peer in peers_cpy) { peer.reject(); } terminated(call.account.bare_jid, null, null); } public void end(string? reason_text = null) { var peers_cpy = new ArrayList(); peers_cpy.add_all(peers.values); if (group_call != null) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream != null) { stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, group_call.muc_jid); } } if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) { foreach (PeerState peer in peers_cpy) { peer.end(Xep.Jingle.ReasonElement.SUCCESS, reason_text); } if (use_cim) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.CallInvites.Module.IDENTITY).send_finish(stream, cim_counterpart, cim_call_id, cim_message_type); } call.state = Call.State.ENDED; } else if (call.state == Call.State.RINGING) { foreach (PeerState peer in peers_cpy) { peer.end(Xep.Jingle.ReasonElement.CANCEL, reason_text); } if (call.direction == Call.DIRECTION_OUTGOING && use_cim) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, cim_counterpart, cim_call_id, cim_message_type); } call.state = Call.State.MISSED; } else { return; } call.end_time = new DateTime.now_utc(); terminated(call.account.bare_jid, null, reason_text); } public void mute_own_audio(bool mute) { we_should_send_audio = !mute; foreach (PeerState peer in peers.values) { peer.mute_own_audio(mute); } } public void mute_own_video(bool mute) { we_should_send_video = !mute; foreach (PeerState peer in peers.values) { peer.mute_own_video(mute); } } public bool should_we_send_video() { return we_should_send_video; } public async void invite_to_call(Jid invitee) { if (this.group_call == null) yield convert_into_group_call(); if (this.group_call == null) return; XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string()); yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner"); stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_propose(stream, cim_call_id, invitee, group_call.muc_jid, we_should_send_video, "chat"); // If the peer hasn't accepted within a minute, retract the invite // TODO this should be unset when we retract the invite. otherwise a second invite attempt might break due to this Timeout.add_seconds(60, () => { if (this == null) return false; bool contains_peer = false; foreach (Jid peer in peers.keys) { if (peer.equals_bare(invitee)) { contains_peer = true; } } if (!contains_peer) { debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string()); // stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, invitee, invite_id); // stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none"); } return false; }); } public Plugins.MediaDevice? get_microphone_device() { if (selected_microphone_device == null) { if (!peers.is_empty) { var audio_stream = peers.values.to_array()[0].get_audio_stream(); selected_microphone_device = call_plugin.get_device(audio_stream, false); } if (selected_microphone_device == null) { selected_microphone_device = call_plugin.get_preferred_device("audio", false); } } return selected_microphone_device; } public Plugins.MediaDevice? get_speaker_device() { if (selected_speaker_device == null) { if (!peers.is_empty) { var audio_stream = peers.values.to_array()[0].get_audio_stream(); selected_speaker_device = call_plugin.get_device(audio_stream, true); } if (selected_speaker_device == null) { selected_speaker_device = call_plugin.get_preferred_device("audio", true); } } return selected_speaker_device; } public Plugins.MediaDevice? get_video_device() { if (selected_video_device == null) { if (!peers.is_empty) { var video_stream = peers.values.to_array()[0].get_video_stream(); selected_video_device = call_plugin.get_device(video_stream, false); } if (selected_video_device == null) { selected_video_device = call_plugin.get_preferred_device("video", false); } } return selected_video_device; } public void set_audio_device(Plugins.MediaDevice? device) { if (device.incoming) { selected_speaker_device = device; } else { selected_microphone_device = device; } foreach (PeerState peer_state in peers.values) { call_plugin.set_device(peer_state.get_audio_stream(), device); } } public void set_video_device(Plugins.MediaDevice? device) { selected_video_device = device; foreach (PeerState peer_state in peers.values) { call_plugin.set_device(peer_state.get_video_stream(), device); } } internal void rename_peer(Jid from_jid, Jid to_jid) { debug("[%s] Renaming %s to %s exists %s", call.account.bare_jid.to_string(), from_jid.to_string(), to_jid.to_string(), peers.has_key(from_jid).to_string()); PeerState? peer_state = peers[from_jid]; if (peer_state == null) return; // Adjust the internal mapping of this `PeerState` object peers.unset(from_jid); peers[to_jid] = peer_state; peer_state.jid = to_jid; } private void on_call_terminated(Jid who_terminated, bool we_terminated, string? reason_name, string? reason_text) { if (call.state == Call.State.RINGING || call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) { call.end_time = new DateTime.now_utc(); } if (call.state == Call.State.IN_PROGRESS) { call.state = Call.State.ENDED; } else if (call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { if (reason_name == Xep.Jingle.ReasonElement.DECLINE) { call.state = Call.State.DECLINED; } else { call.state = Call.State.FAILED; } } terminated(who_terminated, reason_name, reason_text); } private void connect_peer_signals(PeerState peer_state) { peers[peer_state.jid] = peer_state; this.bind_property("we-should-send-audio", peer_state, "we-should-send-audio", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); this.bind_property("we-should-send-video", peer_state, "we-should-send-video", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); this.bind_property("group-call", peer_state, "group-call", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); peer_state.stream_created.connect((peer, media) => { on_peer_stream_created(peer, media); }); peer_state.session_terminated.connect((we_terminated, reason_name, reason_text) => { debug("[%s] Peer left %s: %s %s (%i peers remaining)", call.account.bare_jid.to_string(), reason_text ?? "", reason_name ?? "", peer_state.jid.to_string(), peers.size); handle_peer_left(peer_state, we_terminated, reason_name, reason_text); }); } public async bool can_convert_into_groupcall() { if (peers.size == 0) return false; Jid peer = peers.keys.to_array()[0]; bool peer_has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(call.account, peer, Xep.Muji.NS_URI); bool can_initiate = stream_interactor.get_module(Calls.IDENTITY).can_initiate_groupcall(call.account); return peer_has_feature && can_initiate; } public async void convert_into_group_call() { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; Jid? muc_jid = stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[call.account]; if (muc_jid == null) { warning("Failed to initiate group call: MUC server not known."); return; } if (cim_call_id == null) cim_call_id = Xmpp.random_uuid(); muc_jid = new Jid("%08x@".printf(Random.next_int()) + muc_jid.to_string()); // TODO longer? debug("[%s] Converting call to groupcall %s", call.account.bare_jid.to_string(), muc_jid.to_string()); yield join_group_call(muc_jid); Xep.DataForms.DataForm? data_form = yield stream_interactor.get_module(MucManager.IDENTITY).get_config_form(call.account, muc_jid); if (data_form == null) return; foreach (Xep.DataForms.DataForm.Field field in data_form.fields) { switch (field.var) { case "muc#roomconfig_allowinvites": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = true; } break; case "muc#roomconfig_persistentroom": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = false; } break; case "muc#roomconfig_membersonly": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = true; } break; case "muc#roomconfig_whois": if (field.type_ == Xep.DataForms.DataForm.Type.LIST_SINGLE) { ((Xep.DataForms.DataForm.ListSingleField) field).value = "anyone"; } break; } } yield stream_interactor.get_module(MucManager.IDENTITY).set_config_form(call.account, muc_jid, data_form); foreach (Jid peer_jid in peers.keys) { debug("[%s] Group call inviting %s", call.account.bare_jid.to_string(), peer_jid.to_string()); yield invite_to_call(peer_jid); } } public async void join_group_call(Jid muc_jid) { debug("[%s] Joining group call %s", call.account.bare_jid.to_string(), muc_jid.to_string()); XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; this.group_call = yield stream.get_module(Xep.Muji.Module.IDENTITY).join_call(stream, muc_jid, we_should_send_video); if (this.group_call == null) { warning("[%s] Couldn't join MUJI MUC", call.account.bare_jid.to_string()); return; } this.group_call.peer_joined.connect((jid) => { debug("[%s] Group call peer joined: %s", call.account.bare_jid.to_string(), jid.to_string()); // Newly joined peers have to call us, not the other way round // Maybe they called us already. Accept the call. // (Except for the first peer, we already have a connection to that one.) if (peers.has_key(jid)) { if (!peers[jid].first_peer) { peers[jid].accept(); } // else: Connection to first peer already active } else { var peer_state = new PeerState(jid, call, this, stream_interactor); peer_state.waiting_for_inbound_muji_connection = true; debug("[%s] Waiting for call from %s", call.account.bare_jid.to_string(), jid.to_string()); add_peer(peer_state); } }); this.group_call.peer_left.connect((jid) => { debug("[%s] Group call peer left: %s", call.account.bare_jid.to_string(), jid.to_string()); PeerState? peer_state = peers[jid]; if (peer_state == null) return; peer_state.end(Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC"); handle_peer_left(peer_state, false, Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC"); }); if (group_call.peers_to_connect_to.size > 4) { end("Call too full - P2p calls don't work well with many participants"); return; } // Call all peers that are in the room already foreach (Jid peer_jid in group_call.peers_to_connect_to) { // Don't establish connection if we have one already (the person that invited us to the call) if (peers.has_key(peer_jid)) continue; debug("[%s] Calling %s because they were in the MUC already", call.account.bare_jid.to_string(), peer_jid.to_string()); PeerState peer_state = new PeerState(peer_jid, call, this, stream_interactor); add_peer(peer_state); peer_state.call_resource.begin(peer_jid); } debug("[%s] Finished joining MUJI muc %s", call.account.bare_jid.to_string(), muc_jid.to_string()); } private void handle_peer_left(PeerState peer_state, bool we_terminated, string? reason_name, string? reason_text) { if (!peers.has_key(peer_state.jid)) return; peers.unset(peer_state.jid); if (peers.is_empty) { if (group_call != null) { group_call.leave(stream_interactor.get_stream(call.account)); on_call_terminated(peer_state.jid, we_terminated, null, "All participants have left the call"); } else { on_call_terminated(peer_state.jid, we_terminated, reason_name, reason_text); } } else { peer_left(peer_state.jid, peer_state, reason_name, reason_text); } } }dino-0.4.3/libdino/src/service/call_store.vala0000644000000000000000000000404414452563620020021 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class CallStore : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("call_store"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap calls_by_db_id = new WeakMap(); public static void start(StreamInteractor stream_interactor, Database db) { CallStore m = new CallStore(stream_interactor, db); stream_interactor.add_module(m); } private CallStore(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_call(Call call, Conversation conversation) { call.persist(db); cache_call(call); } public Call? get_call_by_id(int id, Conversation conversation) { Call? call = calls_by_db_id[id]; if (call != null) { return call; } RowOption row_option = db.call.select().with(db.call.id, "=", id).row(); return create_call_from_row_opt(row_option, conversation); } private Call? create_call_from_row_opt(RowOption row_opt, Conversation conversation) { if (!row_opt.is_present()) return null; try { Call call = new Call.from_row(db, row_opt.inner); if (conversation.type_.is_muc_semantic()) { call.ourpart = conversation.counterpart.with_resource(call.ourpart.resourcepart); } cache_call(call); return call; } catch (InvalidJidError e) { warning("Got message with invalid Jid: %s", e.message); } return null; } private void cache_call(Call call) { calls_by_db_id[call.id] = call; } } }dino-0.4.3/libdino/src/service/calls.vala0000644000000000000000000006612614452563620017001 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class Calls : StreamInteractionModule, Object { public signal void call_incoming(Call call, CallState state, Conversation conversation, bool video, bool multiparty); public signal void call_outgoing(Call call, CallState state, Conversation conversation); public signal void call_terminated(Call call, string? reason_name, string? reason_text); public signal void conference_info_received(Call call, Xep.Coin.ConferenceInfo conference_info); public static ModuleIdentity IDENTITY = new ModuleIdentity("calls"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; // public HashMap current_jmi_request_call = new HashMap(Account.hash_func, Account.equals_func); public HashMap jmi_request_peer = new HashMap(Call.hash_func, Call.equals_func); public HashMap call_states = new HashMap(Call.hash_func, Call.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { Calls m = new Calls(stream_interactor, db); stream_interactor.add_module(m); } private Calls(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); } public async CallState? initiate_call(Conversation conversation, bool video) { Call call = new Call(); call.direction = Call.DIRECTION_OUTGOING; call.account = conversation.account; call.counterpart = conversation.counterpart; call.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.full_jid; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; call.state = Call.State.RINGING; stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); var call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); call_state.we_should_send_video = video; call_state.we_should_send_audio = true; if (conversation.type_ == Conversation.Type.CHAT) { call.add_peer(conversation.counterpart); PeerState peer_state = call_state.set_first_peer(conversation.counterpart); jmi_request_peer[call] = peer_state; yield peer_state.initiate_call(conversation.counterpart); } else { call_state.initiate_groupchat_call.begin(conversation.counterpart); } conversation.last_active = call.time; call_outgoing(call, call_state, conversation); return call_state; } public bool can_we_do_calls(Account account) { Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin; if (plugin == null) return false; return plugin.supports(null); } public async bool can_conversation_do_calls(Conversation conversation) { if (!can_we_do_calls(conversation.account)) return false; if (conversation.type_ == Conversation.Type.CHAT) { return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart); } else { bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); return is_private && can_initiate_groupcall(conversation.account); } } public bool can_initiate_groupcall(Account account) { return stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[account] != null; } public async Gee.List get_call_resources(Account account, Jid counterpart) { ArrayList ret = new ArrayList(); XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return ret; Presence.Flag? presence_flag = stream.get_flag(Presence.Flag.IDENTITY); if (presence_flag == null) return ret; Gee.List? full_jids = presence_flag.get_resources(counterpart); if (full_jids == null) return ret; foreach (Jid full_jid in full_jids) { var module = stream.get_module(Xep.JingleRtp.Module.IDENTITY); if (module == null) return ret; bool supports_rtc = yield module.is_available(stream, full_jid); if (!supports_rtc) continue; ret.add(full_jid); } return ret; } public async bool contains_jmi_resources(Account account, Gee.List full_jids) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return false; foreach (Jid full_jid in full_jids) { bool does_jmi = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(account, full_jid, Xep.JingleMessageInitiation.NS_URI); if (does_jmi) return true; } return false; } public bool has_jmi_resources(Jid counterpart) { int64 jmi_resources = db.entity.select() .with(db.entity.jid_id, "=", db.get_jid_id(counterpart)) .join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity) .with(db.entity_feature.feature, "=", Xep.JingleMessageInitiation.NS_URI) .count(); return jmi_resources > 0; } public bool is_call_in_progress() { foreach (Call call in call_states.keys) { if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { return true; } } return false; } private void on_incoming_call(Account account, Xep.Jingle.Session session) { Jid? muji_room = session.muji_room; bool counterpart_wants_video = false; foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) continue; if (rtp_content_parameter.media == "video" && session.senders_include_us(content.senders)) { counterpart_wants_video = true; } } // Check if this comes from a MUJI MUC => accept if (muji_room != null) { debug("[%s] Incoming call from %s from MUJI muc %s", account.bare_jid.to_string(), session.peer_full_jid.to_string(), muji_room.to_string()); foreach (CallState call_state in call_states.values) { if (call_state.call.account.equals(account) && call_state.group_call != null && call_state.group_call.muc_jid.equals(muji_room)) { if (call_state.peers.keys.contains(session.peer_full_jid)) { PeerState peer_state = call_state.peers[session.peer_full_jid]; debug("[%s] Incoming call, we know the peer. Expected %s", account.bare_jid.to_string(), peer_state.waiting_for_inbound_muji_connection.to_string()); if (!peer_state.waiting_for_inbound_muji_connection) return; peer_state.set_session(session); debug(@"[%s] Accepting incoming MUJI call from %s", account.bare_jid.to_string(), session.peer_full_jid.to_string()); peer_state.accept(); } else { debug(@"[%s] Incoming call, but didn't see peer in MUC yet", account.bare_jid.to_string()); PeerState peer_state = new PeerState(session.peer_full_jid, call_state.call, call_state, stream_interactor); peer_state.set_session(session); call_state.add_peer(peer_state); } return; } } return; } debug(@"[%s] Incoming call from %s", account.bare_jid.to_string(), session.peer_full_jid.to_string()); // Check if we already got this call via Jingle Message Initiation => accept // PeerState.accept() checks if the call was accepted and ensures that we don't accidentally send video PeerState? peer_state = get_peer_by_sid(account, session.sid, session.peer_full_jid); if (peer_state != null) { jmi_request_peer[peer_state.call].set_session(session); jmi_request_peer[peer_state.call].accept(); jmi_request_peer.unset(peer_state.call); return; } // This is a direct call without prior JMI. Ask user. if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(session.peer_full_jid.bare_jid, account)) return; peer_state = create_received_call(account, session.peer_full_jid, account.full_jid, counterpart_wants_video); peer_state.set_session(session); Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(peer_state.call.counterpart.bare_jid, account, Conversation.Type.CHAT); call_incoming(peer_state.call, peer_state.call_state, conversation, counterpart_wants_video, false); stream_interactor.module_manager.get_module(account, Xep.JingleRtp.Module.IDENTITY).session_info_type.send_ringing(session); } private PeerState create_received_call(Account account, Jid from, Jid to, bool video_requested) { Call call = new Call(); if (from.equals_bare(account.bare_jid)) { // Call requested by another of our devices call.direction = Call.DIRECTION_OUTGOING; call.ourpart = from; call.state = Call.State.OTHER_DEVICE; call.counterpart = to; } else { call.direction = Call.DIRECTION_INCOMING; call.ourpart = account.full_jid; call.state = Call.State.RINGING; call.counterpart = from; } call.add_peer(call.counterpart); call.account = account; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(call.counterpart.bare_jid, account, Conversation.Type.CHAT); stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); conversation.last_active = call.time; var call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); PeerState peer_state = call_state.set_first_peer(call.counterpart); call_state.we_should_send_video = video_requested; call_state.we_should_send_audio = true; return peer_state; } private CallState? get_call_state_by_call_id(Account account, string call_id, Jid? counterpart_jid = null) { foreach (CallState call_state in call_states.values) { if (!call_state.call.account.equals(account)) continue; if (call_state.cim_call_id == call_id) { if (counterpart_jid == null) return call_state; foreach (Jid jid in call_state.peers.keys) { if (jid.equals_bare(counterpart_jid)) { return call_state; } } } } return null; } private PeerState? get_peer_by_sid(Account account, string sid, Jid jid1, Jid? jid2 = null) { Jid relevant_jid = jid1.equals_bare(account.bare_jid) && jid2 != null ? jid2 : jid1; foreach (CallState call_state in call_states.values) { if (!call_state.call.account.equals(account)) continue; foreach (PeerState peer_state in call_state.peers.values) { if (peer_state.sid != sid) continue; if (peer_state.jid.equals_bare(relevant_jid)) { return peer_state; } } } return null; } private CallState? create_recv_muji_call(Account account, string call_id, Jid inviter_jid, Jid muc_jid, string message_type) { debug("[%s] Muji call received from %s for MUC %s, type %s", account.bare_jid.to_string(), inviter_jid.to_string(), muc_jid.to_string(), message_type); foreach (Call call in call_states.keys) { if (!call.account.equals(account)) continue; CallState call_state = call_states[call]; if (call.counterparts.size == 1 && call.counterparts.contains(inviter_jid) && call_state.accepted) { // A call is converted into a group call. call_state.cim_call_id = call_id; call_state.join_group_call.begin(muc_jid); return null; } } Call call = new Call(); call.direction = Call.DIRECTION_INCOMING; call.ourpart = account.full_jid; call.counterpart = inviter_jid; call.account = account; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; call.state = Call.State.RINGING; // TODO create conv Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(inviter_jid.bare_jid, account); if (conversation == null) return null; stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); conversation.last_active = call.time; CallState call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); call_state.invited_to_group_call = muc_jid; call_state.parent_muc = inviter_jid.bare_jid; debug("[%s] on_muji_call_received accepting", account.bare_jid.to_string()); return call_state; } private void remove_call_from_datastructures(Call call) { jmi_request_peer.unset(call); call_states.unset(call); } private void connect_call_state_signals(CallState call_state) { call_states[call_state.call] = call_state; ulong terminated_handler_id = -1; terminated_handler_id = call_state.terminated.connect((who_terminated, reason_name, reason_text) => { remove_call_from_datastructures(call_state.call); call_terminated(call_state.call, reason_name, reason_text); call_state.disconnect(terminated_handler_id); }); } private void on_account_added(Account account) { Xep.Jingle.Module jingle_module = stream_interactor.module_manager.get_module(account, Xep.Jingle.Module.IDENTITY); jingle_module.session_initiate_received.connect((stream, session) => { foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { on_incoming_call(account, session); break; } } }); Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY); mi_module.session_proposed.connect((from, to, sid, descriptions) => { if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(from.bare_jid, account)) return; bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio"); bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video"); if (!audio_requested && !video_requested) return; PeerState peer_state = create_received_call(account, from, to, video_requested); peer_state.sid = sid; CallState call_state = call_states[peer_state.call]; jmi_request_peer[peer_state.call] = peer_state; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(call_state.call.counterpart.bare_jid, account, Conversation.Type.CHAT); if (call_state.call.direction == Call.DIRECTION_INCOMING) { call_incoming(call_state.call, call_state, conversation, video_requested, false); } else { call_outgoing(call_state.call, call_state, conversation); } }); mi_module.session_accepted.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; // Carboned message from our account if (from.equals_bare(account.bare_jid)) { // Ignore carbon from ourselves if (from.equals(account.full_jid)) return; call.ourpart = from; call.state = Call.State.OTHER_DEVICE; remove_call_from_datastructures(call); return; } // We proposed the call. This is a message from our peer. if (call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(peer_state.jid) && to.equals(account.full_jid)) { // We know the full jid of our peer now call_states[call].rename_peer(jmi_request_peer[call].jid, from); jmi_request_peer[call].call_resource.begin(from); } }); mi_module.session_rejected.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; bool outgoing_reject = call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(call.counterparts[0]); bool incoming_reject = call.direction == Call.DIRECTION_INCOMING && from.equals_bare(account.bare_jid); if (!outgoing_reject && !incoming_reject) return; // We don't care if a single person in a group call rejected the call if (incoming_reject && call_states[call].group_call != null) return; call.state = Call.State.DECLINED; call_states[call].terminated(from, Xep.Jingle.ReasonElement.DECLINE, "JMI reject"); remove_call_from_datastructures(call); }); mi_module.session_retracted.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; bool outgoing_retract = call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(account.bare_jid); bool incoming_retract = call.direction == Call.DIRECTION_INCOMING && from.equals_bare(call.counterpart); if (!(outgoing_retract || incoming_retract)) return; call.state = Call.State.MISSED; call_states[call].terminated(from, Xep.Jingle.ReasonElement.CANCEL, "JMI retract"); remove_call_from_datastructures(call); }); Xep.CallInvites.Module call_invites_module = stream_interactor.module_manager.get_module(account, Xep.CallInvites.Module.IDENTITY); call_invites_module.call_proposed.connect((from_jid, to_jid, call_id, video_requested, join_methods, message_stanza) => { if (from_jid.equals_bare(account.bare_jid)) return; if (stream_interactor.get_module(MucManager.IDENTITY).is_own_muc_jid(from_jid, account)) return; bool multiparty = false; CallState? call_state = null; foreach (StanzaNode join_method_node in join_methods) { if (join_method_node.name == "muji" && join_method_node.ns_uri == Xep.Muji.NS_URI) { // This is a MUJI invite // Disregard calls from muc history DateTime? delay = Xep.DelayedDelivery.get_time_for_message(message_stanza, from_jid.bare_jid); if (delay != null) return; string? room_jid_str = join_method_node.get_attribute("room"); if (room_jid_str == null) return; Jid room_jid = new Jid(room_jid_str); call_state = create_recv_muji_call(account, call_id, from_jid, room_jid, message_stanza.type_); multiparty = true; break; } else if (join_method_node.name == "jingle" && join_method_node.ns_uri == Xep.CallInvites.NS_URI) { // This is an invite for a direct Jingle session if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(from_jid.bare_jid, account)) return; string? sid = join_method_node.get_attribute("sid"); if (sid == null) return; PeerState peer_state = create_received_call(account, from_jid, to_jid, video_requested); peer_state.sid = sid; call_state = call_states[peer_state.call]; jmi_request_peer[call_state.call] = peer_state; break; } } if (call_state == null) return; call_state.we_should_send_audio = true; call_state.we_should_send_video = video_requested; call_state.use_cim = true; call_state.cim_call_id = call_id; call_state.cim_counterpart = message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT ? from_jid.bare_jid : from_jid; call_state.cim_message_type = message_stanza.type_; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(from_jid, to_jid, account, message_stanza.type_); if (conversation == null) return; conversation.last_active = call_state.call.time; if (call_state.call.direction == Call.DIRECTION_INCOMING) { call_incoming(call_state.call, call_state, conversation, video_requested, multiparty); } else { call_outgoing(call_state.call, call_state, conversation); } }); call_invites_module.call_accepted.connect((from_jid, to_jid, call_id, message_type) => { // Carboned message from our account if (from_jid.equals_bare(account.bare_jid)) { CallState? call_state = get_call_state_by_call_id(account, call_id); if (call_state == null) return; Call call = call_state.call; // Ignore carbon from ourselves if (from_jid.equals(account.full_jid)) return; // We accepted the call from another device call.ourpart = from_jid; call.state = Call.State.OTHER_DEVICE; remove_call_from_datastructures(call); return; } CallState? call_state = get_call_state_by_call_id(account, call_id, from_jid); if (call_state == null) return; Call call = call_state.call; // We proposed the call. This is a message from our peer. if (call.direction == Call.DIRECTION_OUTGOING && to_jid.equals(account.full_jid)) { // We know the full jid of our peer now call_state.rename_peer(jmi_request_peer[call].jid, from_jid); jmi_request_peer[call].call_resource.begin(from_jid); } }); call_invites_module.call_retracted.connect((from_jid, to_jid, call_id, message_type) => { if (from_jid.equals_bare(account.bare_jid)) return; // The call was retracted by the counterpart CallState? call_state = get_call_state_by_call_id(account, call_id, from_jid); if (call_state == null) return; if (call_state.call.state != Call.State.RINGING) { debug("%s tried to retract a call that's in state %s. Ignoring.", from_jid.to_string(), call_state.call.state.to_string()); return; } // TODO prevent other MUC occupants from retracting a call call_state.call.state = Call.State.MISSED; remove_call_from_datastructures(call_state.call); }); call_invites_module.call_rejected.connect((from_jid, to_jid, call_id, message_type) => { // We rejected an invite from another device if (from_jid.equals_bare(account.bare_jid)) { CallState? call_state = get_call_state_by_call_id(account, call_id); if (call_state == null) return; Call call = call_state.call; call.state = Call.State.DECLINED; } if (from_jid.equals_bare(account.bare_jid)) return; debug(@"[%s] %s rejected our MUJI invite", account.bare_jid.to_string(), from_jid.to_string()); }); stream_interactor.module_manager.get_module(account, Xep.Coin.Module.IDENTITY).coin_info_received.connect((jid, info) => { foreach (Call call in call_states.keys) { if (call.counterparts[0].equals_bare(jid)) { conference_info_received(call, info); return; } } }); } } }dino-0.4.3/libdino/src/service/chat_interaction.vala0000644000000000000000000002760214452563620021215 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ChatInteraction : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("chat_interaction"); public string id { get { return IDENTITY.id; } } public signal void focused_in(Conversation conversation); public signal void focused_out(Conversation conversation); private StreamInteractor stream_interactor; private Conversation? selected_conversation; private HashMap last_input_interaction = new HashMap(Conversation.hash_func, Conversation.equals_func); private HashMap last_interface_interaction = new HashMap(Conversation.hash_func, Conversation.equals_func); private bool focus_in = false; public static void start(StreamInteractor stream_interactor) { ChatInteraction m = new ChatInteraction(stream_interactor); stream_interactor.add_module(m); } private ChatInteraction(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; Timeout.add_seconds(30, update_interactions); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(stream_interactor)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(on_message_sent); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(new_item); } public int get_num_unread(Conversation conversation) { Database db = Dino.Application.get_default().db; Qlite.QueryBuilder query = db.content_item.select() .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false); ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to_item != null) { string time = read_up_to_item.time.to_unix().to_string(); string id = read_up_to_item.id.to_string(); query.where(@"time > ? OR (time = ? AND id > ?)", { time, time, id }); } // If it's a new conversation with read_up_to_item == null, all items are new. return (int) query.count(); } public bool is_active_focus(Conversation? conversation = null) { if (conversation != null) { return focus_in && conversation.equals(this.selected_conversation); } else { return focus_in; } } public void on_window_focus_in(Conversation? conversation) { on_conversation_focused(conversation); } public void on_window_focus_out(Conversation? conversation) { on_conversation_unfocused(conversation); } public void on_message_entered(Conversation? conversation) { if (!last_input_interaction.has_key(conversation)) { send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_COMPOSING); } last_input_interaction[conversation] = new DateTime.now_utc(); last_interface_interaction[conversation] = new DateTime.now_utc(); } public void on_message_cleared(Conversation? conversation) { if (last_input_interaction.has_key(conversation)) { last_input_interaction.unset(conversation); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_ACTIVE); } } public void on_conversation_selected(Conversation conversation) { on_conversation_unfocused(selected_conversation); selected_conversation = conversation; on_conversation_focused(conversation); } private void new_item(ContentItem item, Conversation conversation) { bool mark_read = is_active_focus(conversation); if (!mark_read) { MessageItem? message_item = item as MessageItem; if (message_item != null) { if (message_item.message.direction == Message.DIRECTION_SENT) { mark_read = true; } } if (message_item == null) { FileItem? file_item = item as FileItem; if (file_item != null) { if (file_item.file_transfer.direction == FileTransfer.DIRECTION_SENT) { mark_read = true; } } } } if (mark_read) { ContentItem? read_up_to = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to != null) { if (read_up_to.compare(item) < 0) { conversation.read_up_to_item = item.id; } } else { conversation.read_up_to_item = item.id; } } } private void on_message_sent(Entities.Message message, Conversation conversation) { last_input_interaction.unset(conversation); last_interface_interaction.unset(conversation); } private void on_conversation_focused(Conversation? conversation) { focus_in = true; if (conversation == null) return; focused_in(conversation); check_send_read(); ContentItem? latest_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); if (latest_item != null) { conversation.read_up_to_item = latest_item.id; } } private void on_conversation_unfocused(Conversation? conversation) { focus_in = false; if (conversation == null) return; focused_out(conversation); if (last_input_interaction.has_key(conversation)) { send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_PAUSED); last_input_interaction.unset(conversation); } } private void check_send_read() { if (selected_conversation == null) return; Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(selected_conversation); if (message != null && message.direction == Entities.Message.DIRECTION_RECEIVED) { send_chat_marker(message, null, selected_conversation, Xep.ChatMarkers.MARKER_DISPLAYED); } } private bool update_interactions() { for (MapIterator iter = last_input_interaction.map_iterator(); iter.has_next(); iter.next()) { if (!iter.valid && iter.has_next()) iter.next(); Conversation conversation = iter.get_key(); if (last_input_interaction.has_key(conversation) && (new DateTime.now_utc()).difference(last_input_interaction[conversation]) >= 15 * TimeSpan.SECOND) { iter.unset(); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_PAUSED); } } for (MapIterator iter = last_interface_interaction.map_iterator(); iter.has_next(); iter.next()) { if (!iter.valid && iter.has_next()) iter.next(); Conversation conversation = iter.get_key(); if (last_interface_interaction.has_key(conversation) && (new DateTime.now_utc()).difference(last_interface_interaction[conversation]) >= 1.5 * TimeSpan.MINUTE) { iter.unset(); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_GONE); } } return true; } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "FILTER_EMPTY", "STORE_CONTENT_ITEM" }; public override string action_group { get { return "OTHER_NODES"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public ReceivedMessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null) return false; ChatInteraction outer = stream_interactor.get_module(ChatInteraction.IDENTITY); outer.send_delivery_receipt(message, stanza, conversation); // Send chat marker if (message.direction == Entities.Message.DIRECTION_SENT) return false; if (outer.is_active_focus(conversation)) { outer.check_send_read(); outer.send_chat_marker(message, stanza, conversation, Xep.ChatMarkers.MARKER_DISPLAYED); } else { outer.send_chat_marker(message, stanza, conversation, Xep.ChatMarkers.MARKER_RECEIVED); } return false; } } private void send_chat_marker(Entities.Message message, Xmpp.MessageStanza? stanza, Conversation conversation, string marker) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; switch (marker) { case Xep.ChatMarkers.MARKER_RECEIVED: if (stanza != null && Xep.ChatMarkers.Module.requests_marking(stanza) && message.type_ != Message.Type.GROUPCHAT) { if (message.stanza_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from, message.stanza_id, message.get_type_string(), Xep.ChatMarkers.MARKER_RECEIVED); } break; case Xep.ChatMarkers.MARKER_DISPLAYED: if (conversation.get_send_marker_setting(stream_interactor) == Conversation.Setting.ON) { if (message.equals(conversation.read_up_to)) return; conversation.read_up_to = message; if (message.type_ == Message.Type.GROUPCHAT || message.type_ == Message.Type.GROUPCHAT_PM) { if (message.server_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from.bare_jid, message.server_id, message.get_type_string(), Xep.ChatMarkers.MARKER_DISPLAYED); } else { if (message.stanza_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from, message.stanza_id, message.get_type_string(), Xep.ChatMarkers.MARKER_DISPLAYED); } } break; } } private void send_delivery_receipt(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (message.direction == Entities.Message.DIRECTION_SENT) return; if (!Xep.MessageDeliveryReceipts.Module.requests_receipt(stanza)) return; if (conversation.type_ == Conversation.Type.GROUPCHAT) return; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { stream.get_module(Xep.MessageDeliveryReceipts.Module.IDENTITY).send_received(stream, message.from, message.stanza_id); } } private void send_chat_state_notification(Conversation conversation, string state) { if (conversation.get_send_typing_setting(stream_interactor) != Conversation.Setting.ON) return; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { string message_type = conversation.type_ == Conversation.Type.GROUPCHAT ? Xmpp.MessageStanza.TYPE_GROUPCHAT : Xmpp.MessageStanza.TYPE_CHAT; stream.get_module(Xep.ChatStateNotifications.Module.IDENTITY).send_state(stream, conversation.counterpart, message_type, state); } } } } dino-0.4.3/libdino/src/service/connection_manager.vala0000644000000000000000000003414714452563620021532 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ConnectionManager : Object { public signal void stream_opened(Account account, XmppStream stream); public signal void stream_attached_modules(Account account, XmppStream stream); public signal void connection_state_changed(Account account, ConnectionState state); public signal void connection_error(Account account, ConnectionError error); public enum ConnectionState { CONNECTED, CONNECTING, DISCONNECTED } private HashMap connections = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_errors = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_ongoing = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_directly_retry = new HashMap(Account.hash_func, Account.equals_func); private NetworkMonitor? network_monitor; private Login1Manager? login1; private ModuleManager module_manager; public string? log_options; public class ConnectionError { public enum Source { CONNECTION, SASL, TLS, STREAM_ERROR } public enum Reconnect { NOW, LATER, NEVER } public Source source; public string? identifier; public Reconnect reconnect_recomendation { get; set; default=Reconnect.NOW; } public ConnectionError(Source source, string? identifier) { this.source = source; this.identifier = identifier; } } private class Connection { public string uuid { get; set; } public XmppStream? stream { get; set; } public ConnectionState connection_state { get; set; default = ConnectionState.DISCONNECTED; } public DateTime? established { get; set; } public DateTime? last_activity { get; set; } public Connection() { reset(); } public void reset() { if (stream != null) { stream.detach_modules(); stream.disconnect.begin(); } stream = null; established = last_activity = null; uuid = Xmpp.random_uuid(); } public void make_offline() { Xmpp.Presence.Stanza presence = new Xmpp.Presence.Stanza(); presence.type_ = Xmpp.Presence.Stanza.TYPE_UNAVAILABLE; if (stream != null) { stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence); } } public async void disconnect_account() { make_offline(); if (stream != null) { try { yield stream.disconnect(); } catch (Error e) { debug("Error disconnecting stream: %s", e.message); } } } } public ConnectionManager(ModuleManager module_manager) { this.module_manager = module_manager; network_monitor = GLib.NetworkMonitor.get_default(); if (network_monitor != null) { network_monitor.network_changed.connect(on_network_changed); network_monitor.notify["connectivity"].connect(on_network_changed); } get_login1.begin((_, res) => { login1 = get_login1.end(res); if (login1 != null) { login1.PrepareForSleep.connect(on_prepare_for_sleep); } }); Timeout.add_seconds(60, () => { foreach (Account account in connections.keys) { if (connections[account].last_activity != null && connections[account].last_activity.compare(new DateTime.now_utc().add_minutes(-1)) < 0) { check_reconnect(account); } } return true; }); } public XmppStream? get_stream(Account account) { if (get_state(account) == ConnectionState.CONNECTED) { return connections[account].stream; } return null; } public ConnectionState get_state(Account account) { if (connections.has_key(account)){ return connections[account].connection_state; } return ConnectionState.DISCONNECTED; } public ConnectionError? get_error(Account account) { if (connection_errors.has_key(account)) { return connection_errors[account]; } return null; } public Collection get_managed_accounts() { return connections.keys; } public void connect_account(Account account) { if (!connections.has_key(account)) { connections[account] = new Connection(); connection_ongoing[account] = false; connection_directly_retry[account] = false; connect_stream.begin(account); } else { check_reconnect(account); } } public void make_offline_all() { foreach (Account account in connections.keys) { make_offline(account); } } private void make_offline(Account account) { connections[account].make_offline(); change_connection_state(account, ConnectionState.DISCONNECTED); } public async void disconnect_account(Account account) { if (connections.has_key(account)) { make_offline(account); connections[account].disconnect_account.begin(); connections.unset(account); } } private async void connect_stream(Account account, string? resource = null) { if (!connections.has_key(account)) return; debug("[%s] (Maybe) Establishing a new connection", account.bare_jid.to_string()); connection_errors.unset(account); if (resource == null) resource = account.resourcepart; XmppStreamResult stream_result; if (connection_ongoing[account]) { debug("[%s] Connection attempt already in progress. Directly retry if it fails.", account.bare_jid.to_string()); connection_directly_retry[account] = true; return; } else if (connections[account].stream != null) { debug("[%s] Cancelling connecting because there is already a stream", account.bare_jid.to_string()); return; } else { connection_ongoing[account] = true; connection_directly_retry[account] = false; change_connection_state(account, ConnectionState.CONNECTING); stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account, resource), log_options, (peer_cert, errors) => { return on_invalid_certificate(account.domainpart, peer_cert, errors); } ); connections[account].stream = stream_result.stream; connection_ongoing[account] = false; } if (stream_result.stream == null) { if (stream_result.tls_errors != null) { set_connection_error(account, new ConnectionError(ConnectionError.Source.TLS, null) { reconnect_recomendation=ConnectionError.Reconnect.NEVER}); return; } debug("[%s] Could not connect", account.bare_jid.to_string()); change_connection_state(account, ConnectionState.DISCONNECTED); check_reconnect(account, connection_directly_retry[account]); return; } XmppStream stream = stream_result.stream; debug("[%s] New connection with resource %s: %p", account.bare_jid.to_string(), resource, stream); connections[account].established = new DateTime.now_utc(); stream.attached_modules.connect((stream) => { stream_attached_modules(account, stream); change_connection_state(account, ConnectionState.CONNECTED); // stream.get_module(Xep.Muji.Module.IDENTITY).join_call(stream, new Jid("test@muc.poez.io"), true); }); stream.get_module(Sasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => { set_connection_error(account, new ConnectionError(ConnectionError.Source.SASL, null)); }); string connection_uuid = connections[account].uuid; stream.received_node.connect(() => { if (connections[account].uuid == connection_uuid) { connections[account].last_activity = new DateTime.now_utc(); } else { warning("Got node for outdated connection"); } }); stream_opened(account, stream); try { yield stream.loop(); } catch (Error e) { debug("[%s %p] Connection error: %s", account.bare_jid.to_string(), stream, e.message); change_connection_state(account, ConnectionState.DISCONNECTED); connections[account].reset(); StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY); if (flag != null) { warning(@"[%s %p] Stream Error: %s", account.bare_jid.to_string(), stream, flag.error_type); set_connection_error(account, new ConnectionError(ConnectionError.Source.STREAM_ERROR, flag.error_type)); if (flag.resource_rejected) { connect_stream.begin(account, account.resourcepart + "-" + random_uuid()); return; } } ConnectionError? error = connection_errors[account]; if (error != null && error.source == ConnectionError.Source.SASL) { return; } check_reconnect(account); } } private void check_reconnects() { foreach (Account account in connections.keys) { check_reconnect(account); } } private void check_reconnect(Account account, bool directly_reconnect = false) { if (!connections.has_key(account)) return; bool acked = false; DateTime? last_activity_was = connections[account].last_activity; if (connections[account].stream == null) { Timeout.add_seconds(10, () => { if (!connections.has_key(account)) return false; if (connections[account].stream != null) return false; if (connections[account].last_activity != last_activity_was) return false; connect_stream.begin(account); return false; }); return; } XmppStream stream = connections[account].stream; stream.get_module(Xep.Ping.Module.IDENTITY).send_ping.begin(stream, account.bare_jid.domain_jid, () => { acked = true; if (connections[account].stream != stream) return; change_connection_state(account, ConnectionState.CONNECTED); }); Timeout.add_seconds(10, () => { if (!connections.has_key(account)) return false; if (connections[account].stream != stream) return false; if (acked) return false; if (connections[account].last_activity != last_activity_was) return false; // Reconnect. Nothing gets through the stream. debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream); change_connection_state(account, ConnectionState.DISCONNECTED); connections[account].reset(); connect_stream.begin(account); return false; }); } private bool network_is_online() { /* FIXME: We should also check for connectivity eventually. For more * details on why we don't do it for now, see: * * - https://github.com/dino/dino/pull/236#pullrequestreview-86851793 * - https://bugzilla.gnome.org/show_bug.cgi?id=792240 */ return network_monitor != null && network_monitor.network_available; } private void on_network_changed() { if (network_is_online()) { debug("NetworkMonitor: Network reported online"); check_reconnects(); } else { debug("NetworkMonitor: Network reported offline"); foreach (Account account in connections.keys) { change_connection_state(account, ConnectionState.DISCONNECTED); } } } private async void on_prepare_for_sleep(bool suspend) { foreach (Account account in connections.keys) { change_connection_state(account, ConnectionState.DISCONNECTED); } if (suspend) { debug("Login1: Device suspended"); foreach (Account account in connections.keys) { try { make_offline(account); if (connections[account].stream != null) { yield connections[account].stream.disconnect(); } } catch (Error e) { debug("Error disconnecting stream %p: %s", connections[account].stream, e.message); } } } else { debug("Login1: Device un-suspend"); check_reconnects(); } } private void change_connection_state(Account account, ConnectionState state) { if (connections.has_key(account)) { connections[account].connection_state = state; connection_state_changed(account, state); } } private void set_connection_error(Account account, ConnectionError error) { connection_errors[account] = error; connection_error(account, error); } public static bool on_invalid_certificate(string domain, TlsCertificate peer_cert, TlsCertificateFlags errors) { if (domain.has_suffix(".onion") && errors == TlsCertificateFlags.UNKNOWN_CA) { // It's barely possible for .onion servers to provide a non-self-signed cert. // But that's fine because encryption is provided independently though TOR. warning("Accepting TLS certificate from unknown CA from .onion address %s", domain); return true; } return false; } } } dino-0.4.3/libdino/src/service/content_item_store.vala0000644000000000000000000004313614452563620021603 0ustar rootrootusing Gee; using Dino.Entities; using Qlite; using Xmpp; namespace Dino { public class ContentItemStore : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("content_item_store"); public string id { get { return IDENTITY.id; } } public signal void new_item(ContentItem item, Conversation conversation); private StreamInteractor stream_interactor; private Database db; private HashMap collection_conversations = new HashMap(Conversation.hash_func, Conversation.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { ContentItemStore m = new ContentItemStore(stream_interactor, db); stream_interactor.add_module(m); } public ContentItemStore(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.get_module(FileManager.IDENTITY).received_file.connect(insert_file_transfer); stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect(announce_message); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(announce_message); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect(insert_call); stream_interactor.get_module(Calls.IDENTITY).call_outgoing.connect(insert_call); } public void init(Conversation conversation, ContentItemCollection item_collection) { collection_conversations[conversation] = item_collection; } public void uninit(Conversation conversation, ContentItemCollection item_collection) { collection_conversations.unset(conversation); } private Gee.List get_items_from_query(QueryBuilder select, Conversation conversation) { Gee.TreeSet items = new Gee.TreeSet(ContentItem.compare_func); foreach (var row in select) { ContentItem content_item = get_item_from_row(row, conversation); items.add(content_item); } Gee.List ret = new ArrayList(); foreach (ContentItem item in items) { ret.add(item); } return ret; } private ContentItem get_item_from_row(Row row, Conversation conversation) throws Error { int id = row[db.content_item.id]; int content_type = row[db.content_item.content_type]; int foreign_id = row[db.content_item.foreign_id]; DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]); return get_item(conversation, id, content_type, foreign_id, time); } private ContentItem get_item(Conversation conversation, int id, int content_type, int foreign_id, DateTime time) throws Error { switch (content_type) { case 1: Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(foreign_id, conversation); if (message != null) { var message_item = new MessageItem(message, conversation, id); message_item.time = time; // In case of message corrections, the original time should be used return message_item; } break; case 2: FileTransfer? file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_id(foreign_id, conversation); if (file_transfer != null) { Message? message = null; if (file_transfer.provider == 0 && file_transfer.info != null) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); } var file_item = new FileItem(file_transfer, conversation, id, message); return file_item; } break; case 3: Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(foreign_id, conversation); if (call != null) { var call_item = new CallItem(call, conversation, id); return call_item; } break; default: warning("Unknown content item type: %i", content_type); break; } throw new Error(-1, 0, "Bad content type %i or non existing content item %i", content_type, foreign_id); } public ContentItem? get_item_by_foreign(Conversation conversation, int type, int foreign_id) { QueryBuilder select = db.content_item.select() .with(db.content_item.content_type, "=", type) .with(db.content_item.foreign_id, "=", foreign_id); Gee.List item = get_items_from_query(select, conversation); return item.size > 0 ? item[0] : null; } public ContentItem? get_item_by_id(Conversation conversation, int id) { QueryBuilder select = db.content_item.select() .with(db.content_item.id, "=", id); Gee.List item = get_items_from_query(select, conversation); return item.size > 0 ? item[0] : null; } public string? get_message_id_for_content_item(Conversation conversation, ContentItem content_item) { Message? message = get_message_for_content_item(conversation, content_item); if (message == null) return null; if (message.edit_to != null) return message.edit_to; if (conversation.type_ == Conversation.Type.CHAT) { return message.stanza_id; } else { return message.server_id; } } public Jid? get_message_sender_for_content_item(Conversation conversation, ContentItem content_item) { Message? message = get_message_for_content_item(conversation, content_item); if (message == null) return null; // No need to look at edit_to, because it's the same sender JID. return message.from; } public Message? get_message_for_content_item(Conversation conversation, ContentItem content_item) { FileItem? file_item = content_item as FileItem; if (file_item != null) { if (file_item.file_transfer.provider != 0 || file_item.file_transfer.info == null) return null; int message_db_id = int.parse(file_item.file_transfer.info); return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message_db_id, conversation); } MessageItem? message_item = content_item as MessageItem; if (message_item != null) { return message_item.message; } return null; } public ContentItem? get_content_item_for_message_id(Conversation conversation, string message_id) { Row? row = get_content_item_row_for_message_id(conversation, message_id); if (row != null) { return get_item_from_row(row, conversation); } return null; } public int get_content_item_id_for_message_id(Conversation conversation, string message_id) { Row? row = get_content_item_row_for_message_id(conversation, message_id); if (row != null) { return row[db.content_item.id]; } return -1; } private Row? get_content_item_row_for_message_id(Conversation conversation, string message_id) { var content_item_row = db.content_item.select(); Message? message = null; if (conversation.type_ == Conversation.Type.CHAT) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_id, conversation); } else { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(message_id, conversation); } if (message == null) return null; RowOption file_transfer_row = db.file_transfer.select() .with(db.file_transfer.account_id, "=", conversation.account.id) .with(db.file_transfer.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.file_transfer.info, "=", message.id.to_string()) .order_by(db.file_transfer.time, "DESC") .single().row(); if (file_transfer_row.is_present()) { content_item_row.with(db.content_item.foreign_id, "=", file_transfer_row[db.file_transfer.id]) .with(db.content_item.content_type, "=", 2); } else { content_item_row.with(db.content_item.foreign_id, "=", message.id) .with(db.content_item.content_type, "=", 1); } RowOption content_item_row_option = content_item_row.single().row(); if (content_item_row_option.is_present()) { return content_item_row_option.inner; } return null; } public ContentItem? get_latest(Conversation conversation) { Gee.List items = get_n_latest(conversation, 1); if (items.size > 0) { return items.get(0); } return null; } public Gee.List get_n_latest(Conversation conversation, int count) { QueryBuilder select = db.content_item.select() .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "DESC") .order_by(db.content_item.id, "DESC") .limit(count); return get_items_from_query(select, conversation); } // public Gee.List get_latest_meta(Conversation conversation, int count) { // QueryBuilder select = db.content_item.select() // .with(db.content_item.conversation_id, "=", conversation.id) // .with(db.content_item.hide, "=", false) // .order_by(db.content_item.time, "DESC") // .order_by(db.content_item.id, "DESC") // .limit(count); // // var ret = new ArrayList(); // foreach (var row in select) { // var item_meta = new ContentItemMeta() { // id = row[db.content_item.id], // content_type = row[db.content_item.content_type], // foreign_id = row[db.content_item.foreign_id], // time = new DateTime.from_unix_utc(row[db.content_item.time]) // }; // } // return ret; // } public Gee.List get_before(Conversation conversation, ContentItem item, int count) { long time = (long) item.time.to_unix(); QueryBuilder select = db.content_item.select() .where(@"time < ? OR (time = ? AND id < ?)", { time.to_string(), time.to_string(), item.id.to_string() }) .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "DESC") .order_by(db.content_item.id, "DESC") .limit(count); return get_items_from_query(select, conversation); } public Gee.List get_after(Conversation conversation, ContentItem item, int count) { long time = (long) item.time.to_unix(); QueryBuilder select = db.content_item.select() .where(@"time > ? OR (time = ? AND id > ?)", { time.to_string(), time.to_string(), item.id.to_string() }) .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "ASC") .order_by(db.content_item.id, "ASC") .limit(count); return get_items_from_query(select, conversation); } public void insert_message(Message message, Conversation conversation, bool hide = false) { MessageItem item = new MessageItem(message, conversation, -1); item.id = db.add_content_item(conversation, message.time, message.local_time, 1, message.id, hide); } private void announce_message(Message message, Conversation conversation) { QueryBuilder select = db.content_item.select(); select.with(db.content_item.foreign_id, "=", message.id); select.with(db.content_item.content_type, "=", 1); select.with(db.content_item.hide, "=", false); foreach (Row row in select) { MessageItem item = new MessageItem(message, conversation, row[db.content_item.id]); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); break; } } private void insert_file_transfer(FileTransfer file_transfer, Conversation conversation) { FileItem item = new FileItem(file_transfer, conversation, -1); item.id = db.add_content_item(conversation, file_transfer.time, file_transfer.local_time, 2, file_transfer.id, false); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); } private void insert_call(Call call, CallState call_state, Conversation conversation) { CallItem item = new CallItem(call, conversation, -1); item.id = db.add_content_item(conversation, call.time, call.local_time, 3, call.id, false); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); } public bool get_item_hide(ContentItem content_item) { return db.content_item.row_with(db.content_item.id, content_item.id)[db.content_item.hide, false]; } public void set_item_hide(ContentItem content_item, bool hide) { db.content_item.update() .with(db.content_item.id, "=", content_item.id) .set(db.content_item.hide, hide) .perform(); } } public interface ContentItemCollection : Object { public abstract void insert_item(ContentItem item); public abstract void remove_item(ContentItem item); } public abstract class ContentItem : Object { public int id { get; set; } public string type_ { get; set; } public Jid jid { get; set; } public DateTime time { get; set; } public Encryption encryption { get; set; } public Entities.Message.Marked mark { get; set; } ContentItem(int id, string ty, Jid jid, DateTime time, Encryption encryption, Entities.Message.Marked mark) { this.id = id; this.type_ = ty; this.jid = jid; this.time = time; this.encryption = encryption; this.mark = mark; } public int compare(ContentItem c) { return compare_func(this, c); } public static int compare_func(ContentItem a, ContentItem b) { int res = a.time.compare(b.time); if (res == 0) { res = a.id - b.id > 0 ? 1 : -1; } return res; } } public class MessageItem : ContentItem { public const string TYPE = "message"; public Message message; public Conversation conversation; public MessageItem(Message message, Conversation conversation, int id) { base(id, TYPE, message.from, message.time, message.encryption, message.marked); this.message = message; this.conversation = conversation; message.bind_property("marked", this, "mark"); } } public class FileItem : ContentItem { public const string TYPE = "file"; public FileTransfer file_transfer; public Conversation conversation; public FileItem(FileTransfer file_transfer, Conversation conversation, int id, Message? message = null) { Entities.Message.Marked mark = Entities.Message.Marked.NONE; if (message != null) { mark = message.marked; } else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) { mark = file_to_message_state(file_transfer.state); } base(id, TYPE, file_transfer.from, file_transfer.time, file_transfer.encryption, mark); this.file_transfer = file_transfer; this.conversation = conversation; // TODO those don't work if (message != null) { message.bind_property("marked", this, "mark"); } else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) { file_transfer.bind_property("state", this, "mark", BindingFlags.DEFAULT, (_, from_value, ref to_value) => { to_value = file_to_message_state((FileTransfer.State)from_value.get_enum()); return true; }); } } private static Entities.Message.Marked file_to_message_state(FileTransfer.State state) { switch (state) { case FileTransfer.State.IN_PROGRESS: return Entities.Message.Marked.UNSENT; case FileTransfer.State.COMPLETE: return Entities.Message.Marked.NONE; case FileTransfer.State.NOT_STARTED: return Entities.Message.Marked.UNSENT; case FileTransfer.State.FAILED: return Entities.Message.Marked.WONTSEND; } assert_not_reached(); } } public class CallItem : ContentItem { public const string TYPE = "call"; public Call call; public Conversation conversation; public CallItem(Call call, Conversation conversation, int id) { base(id, TYPE, call.proposer, call.time, call.encryption, Message.Marked.NONE); this.call = call; this.conversation = conversation; call.bind_property("encryption", this, "encryption"); } } } dino-0.4.3/libdino/src/service/conversation_manager.vala0000644000000000000000000002133314452563620022076 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ConversationManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("conversation_manager"); public string id { get { return IDENTITY.id; } } public signal void conversation_activated(Conversation conversation); public signal void conversation_deactivated(Conversation conversation); private StreamInteractor stream_interactor; private Database db; private HashMap>> conversations = new HashMap>>(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { ConversationManager m = new ConversationManager(stream_interactor, db); stream_interactor.add_module(m); } private ConversationManager(StreamInteractor stream_interactor, Database db) { this.db = db; this.stream_interactor = stream_interactor; stream_interactor.add_module(this); stream_interactor.account_added.connect(on_account_added); stream_interactor.account_removed.connect(on_account_removed); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new MessageListener(stream_interactor)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(handle_sent_message); } public Conversation create_conversation(Jid jid, Account account, Conversation.Type? type = null) { assert(conversations.has_key(account)); Jid store_jid = type == Conversation.Type.GROUPCHAT ? jid.bare_jid : jid; // Do we already have a conversation for this jid? if (conversations[account].has_key(store_jid)) { foreach (var conversation in conversations[account][store_jid]) { if (conversation.type_ == type) { return conversation; } } } // Create a new converation Conversation conversation = new Conversation(jid, account, type); add_conversation(conversation); conversation.persist(db); return conversation; } public Conversation? get_conversation_for_message(Entities.Message message) { if (message.type_ == Entities.Message.Type.CHAT) { return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.CHAT); } else if (message.type_ == Entities.Message.Type.GROUPCHAT) { return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.GROUPCHAT); } else if (message.type_ == Entities.Message.Type.GROUPCHAT_PM) { return create_conversation(message.counterpart, message.account, Conversation.Type.GROUPCHAT_PM); } return null; } public Gee.List get_conversations(Jid jid, Account account) { Gee.List ret = new ArrayList(Conversation.equals_func); Conversation? bare_conversation = get_conversation(jid, account); if (bare_conversation != null) ret.add(bare_conversation); Conversation? full_conversation = get_conversation(jid.bare_jid, account); if (full_conversation != null) ret.add(full_conversation); return ret; } public Conversation? get_conversation(Jid jid, Account account, Conversation.Type? type = null) { if (conversations.has_key(account)) { if (conversations[account].has_key(jid)) { foreach (var conversation in conversations[account][jid]) { if (type == null || conversation.type_ == type) { return conversation; } } } } return null; } public Conversation? approx_conversation_for_stanza(Jid from, Jid to, Account account, string msg_ty) { if (msg_ty == Xmpp.MessageStanza.TYPE_GROUPCHAT) { return get_conversation(from.bare_jid, account, Conversation.Type.GROUPCHAT); } Jid counterpart = from.equals_bare(account.bare_jid) ? to : from; if (msg_ty == Xmpp.MessageStanza.TYPE_CHAT && counterpart.is_full() && get_conversation(counterpart.bare_jid, account, Conversation.Type.GROUPCHAT) != null) { var pm = get_conversation(counterpart, account, Conversation.Type.GROUPCHAT_PM); if (pm != null) return pm; } return get_conversation(counterpart.bare_jid, account, Conversation.Type.CHAT); } public Conversation? get_conversation_by_id(int id) { foreach (HashMap> hm in conversations.values) { foreach (Gee.List hm2 in hm.values) { foreach (Conversation conversation in hm2) { if (conversation.id == id) { return conversation; } } } } return null; } public Gee.List get_active_conversations(Account? account = null) { Gee.List ret = new ArrayList(Conversation.equals_func); foreach (Account account_ in conversations.keys) { if (account != null && !account_.equals(account)) continue; foreach (Gee.List list in conversations[account_].values) { foreach (var conversation in list) { if(conversation.active) ret.add(conversation); } } } return ret; } public void start_conversation(Conversation conversation) { if (conversation.last_active == null) { conversation.last_active = new DateTime.now_utc(); if (conversation.active) conversation_activated(conversation); } if (!conversation.active) { conversation.active = true; conversation_activated(conversation); } } public void close_conversation(Conversation conversation) { if (!conversation.active) return; conversation.active = false; conversation_deactivated(conversation); } private void on_account_added(Account account) { conversations[account] = new HashMap>(Jid.hash_func, Jid.equals_func); foreach (Conversation conversation in db.get_conversations(account)) { add_conversation(conversation); } } private void on_account_removed(Account account) { foreach (Gee.List list in conversations[account].values) { foreach (var conversation in list) { if(conversation.active) conversation_deactivated(conversation); } } conversations.unset(account); } private class MessageListener : Dino.MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "FILTER_EMPTY" }; public override string action_group { get { return "MANAGER"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public MessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { conversation.last_active = message.time; if (stanza != null) { bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null; bool is_recent = message.time.compare(new DateTime.now_utc().add_days(-3)) > 0; if (is_mam_message && !is_recent) return false; } stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); return false; } } private void handle_sent_message(Entities.Message message, Conversation conversation) { conversation.last_active = message.time; bool is_recent = message.time.compare(new DateTime.now_utc().add_hours(-24)) > 0; if (is_recent) { start_conversation(conversation); } } private void add_conversation(Conversation conversation) { if (!conversations[conversation.account].has_key(conversation.counterpart)) { conversations[conversation.account][conversation.counterpart] = new ArrayList(Conversation.equals_func); } conversations[conversation.account][conversation.counterpart].add(conversation); if (conversation.active) { conversation_activated(conversation); } } } } dino-0.4.3/libdino/src/service/counterpart_interaction_manager.vala0000644000000000000000000002710314452563620024332 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class CounterpartInteractionManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("counterpart_interaction_manager"); public string id { get { return IDENTITY.id; } } public signal void received_state(Conversation conversation, string state); public signal void received_marker(Account account, Jid jid, Entities.Message message, Entities.Message.Marked marker); public signal void received_message_received(Account account, Jid jid, Entities.Message message); public signal void received_message_displayed(Account account, Jid jid, Entities.Message message); private StreamInteractor stream_interactor; private HashMap> typing_since = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap marker_wo_message = new HashMap(); public static void start(StreamInteractor stream_interactor) { CounterpartInteractionManager m = new CounterpartInteractionManager(stream_interactor); stream_interactor.add_module(m); } private CounterpartInteractionManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect((message, conversation) => clear_chat_state(conversation, message.from)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent_or_received.connect(check_if_got_marker); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect((jid, account) => { foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_conversations(jid, account)) { clear_chat_state(conversation, jid); } }); stream_interactor.stream_negotiated.connect((account) => clear_all_chat_states(account) ); Timeout.add_seconds(60, () => { var one_min_ago = new DateTime.now_utc().add_seconds(-1); foreach (Conversation conversation in typing_since.keys) { ArrayList to_remove = new ArrayList(); foreach (Jid jid in typing_since[conversation].keys) { if (typing_since[conversation][jid].compare(one_min_ago) < 0) { to_remove.add(jid); } } foreach (Jid jid in to_remove) { clear_chat_state(conversation, jid); } } return true; }); } public Gee.List? get_typing_jids(Conversation conversation) { if (stream_interactor.connection_manager.get_state(conversation.account) != ConnectionManager.ConnectionState.CONNECTED) return null; if (!typing_since.has_key(conversation) || typing_since[conversation].size == 0) return null; var jids = new ArrayList(); foreach (Jid jid in typing_since[conversation].keys) { jids.add(jid); } return jids; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.ChatMarkers.Module.IDENTITY).marker_received.connect( (stream, jid, marker, id, message_stanza) => { on_chat_marker_received.begin(account, jid, marker, id, message_stanza); }); stream_interactor.module_manager.get_module(account, Xep.MessageDeliveryReceipts.Module.IDENTITY).receipt_received.connect((stream, jid, id, stanza) => { on_receipt_received(account, jid, id, stanza); }); stream_interactor.module_manager.get_module(account, Xep.ChatStateNotifications.Module.IDENTITY).chat_state_received.connect((stream, jid, state, stanza) => { on_chat_state_received.begin(account, jid, state, stanza); }); } private void clear_chat_state(Conversation conversation, Jid jid) { if (!(typing_since.has_key(conversation) && typing_since[conversation].has_key(jid))) return; typing_since[conversation].unset(jid); received_state(conversation, Xmpp.Xep.ChatStateNotifications.STATE_ACTIVE); } private void clear_all_chat_states(Account account) { foreach (Conversation conversation in typing_since.keys) { if (conversation.account.equals(account)) { received_state(conversation, Xmpp.Xep.ChatStateNotifications.STATE_ACTIVE); typing_since[conversation].clear(); } } } private async void on_chat_state_received(Account account, Jid jid, string state, MessageStanza stanza) { // Don't show our own (other devices) typing notification if (jid.equals_bare(account.bare_jid)) return; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(stanza.from, stanza.to, account, stanza.type_); if (conversation == null) return; // Don't show our own typing notification in MUCs if (conversation.type_ == Conversation.Type.GROUPCHAT) { Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(jid.bare_jid, account); if (own_muc_jid != null && own_muc_jid.equals(jid)) { return; } } if (!typing_since.has_key(conversation)) { typing_since[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } if (state == Xmpp.Xep.ChatStateNotifications.STATE_COMPOSING) { typing_since[conversation][jid] = new DateTime.now_utc(); received_state(conversation, state); } else { clear_chat_state(conversation, jid); } } private async void on_chat_marker_received(Account account, Jid jid, string marker, string stanza_id, MessageStanza message_stanza) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(message_stanza.from, message_stanza.to, account, message_stanza.type_); if (conversation == null) return; handle_chat_marker(conversation, jid, marker, stanza_id); } private void handle_chat_marker(Conversation conversation, Jid jid, string marker, string stanza_id) { // Check if the marker comes from ourselves (own jid or our jid in a MUC) bool own_marker = false; if (conversation.type_ == Conversation.Type.CHAT) { own_marker = conversation.account.bare_jid.to_string() == jid.bare_jid.to_string(); } else { Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(jid.bare_jid, conversation.account); if (own_muc_jid != null && own_muc_jid.equals(jid)) { own_marker = true; } } if (own_marker) { // If we received a display marker from ourselves (other device), set the conversation read up to that message. if (marker != Xep.ChatMarkers.MARKER_DISPLAYED && marker != Xep.ChatMarkers.MARKER_ACKNOWLEDGED) return; Entities.Message? message = null; if (conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(stanza_id, conversation); // Outdated clients might use the message id. Or in MUCs that don't send server ids. if (message == null) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); } } else { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); } if (message == null) return; // Don't move read marker backwards because we get old info from another client if (conversation.read_up_to != null && conversation.read_up_to.local_time.compare(message.local_time) > 0) return; conversation.read_up_to = message; // TODO: This only marks messages as read, not http file transfers. ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); if (content_item == null) return; ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to_item != null && read_up_to_item.compare(content_item) > 0) return; conversation.read_up_to_item = content_item.id; } else { // We can't currently handle chat markers in MUCs if (conversation.type_ == Conversation.Type.GROUPCHAT) return; Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); if (message != null) { switch (marker) { case Xep.ChatMarkers.MARKER_RECEIVED: // If we got a received marker, mark the respective message received. received_message_received(conversation.account, jid, message); message.marked = Entities.Message.Marked.RECEIVED; break; case Xep.ChatMarkers.MARKER_DISPLAYED: // If we got a display marker, set all messages up to that message as read (if we know they've been received). received_message_displayed(conversation.account, jid, message); Gee.List messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation); foreach (Entities.Message m in messages) { if (m.equals(message)) break; if (m.marked == Entities.Message.Marked.RECEIVED) m.marked = Entities.Message.Marked.READ; } message.marked = Entities.Message.Marked.READ; break; } } else { // We might get a marker before the actual message (on catchup). Save the marker. if (marker_wo_message.has_key(stanza_id) && marker_wo_message[stanza_id] == Xep.ChatMarkers.MARKER_DISPLAYED && marker == Xep.ChatMarkers.MARKER_RECEIVED) { return; } marker_wo_message[stanza_id] = marker; } } } private void check_if_got_marker(Entities.Message message, Conversation conversation) { if (marker_wo_message.has_key(message.stanza_id)) { handle_chat_marker(conversation, conversation.counterpart, marker_wo_message[message.stanza_id], message.stanza_id); marker_wo_message.unset(message.stanza_id); } } private void on_receipt_received(Account account, Jid jid, string id, MessageStanza stanza) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(stanza.from, stanza.to, account, stanza.type_); if (conversation == null) return; handle_chat_marker(conversation, jid,Xep.ChatMarkers.MARKER_RECEIVED, id); } } } dino-0.4.3/libdino/src/service/database.vala0000644000000000000000000010524714452563620017445 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Dino.Entities; namespace Dino { public class Database : Qlite.Database { private const int VERSION = 26; public class AccountTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column bare_jid = new Column.Text("bare_jid") { unique = true, not_null = true }; public Column resourcepart = new Column.Text("resourcepart"); public Column password = new Column.Text("password"); public Column alias = new Column.Text("alias"); public Column enabled = new Column.BoolInt("enabled"); public Column roster_version = new Column.Text("roster_version") { min_version=2 }; public Column mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 }; internal AccountTable(Database db) { base(db, "account"); init({id, bare_jid, resourcepart, password, alias, enabled, roster_version, mam_earliest_synced}); } } public class JidTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column bare_jid = new Column.Text("bare_jid") { unique = true, not_null = true }; internal JidTable(Database db) { base(db, "jid"); init({id, bare_jid}); } } public class EntityTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id"); public Column jid_id = new Column.Integer("jid_id"); public Column resource = new Column.Text("resource"); public Column caps_hash = new Column.Text("caps_hash"); public Column last_seen = new Column.Long("last_seen"); internal EntityTable(Database db) { base(db, "entity"); init({id, account_id, jid_id, resource, caps_hash, last_seen}); unique({account_id, jid_id, resource}, "IGNORE"); } } public class ContentItemTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column conversation_id = new Column.Integer("conversation_id") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column local_time = new Column.Long("local_time") { not_null = true }; public Column content_type = new Column.Integer("content_type") { not_null = true }; public Column foreign_id = new Column.Integer("foreign_id") { not_null = true }; public Column hide = new Column.BoolInt("hide") { default = "0", not_null = true, min_version = 9 }; internal ContentItemTable(Database db) { base(db, "content_item"); init({id, conversation_id, time, local_time, content_type, foreign_id, hide}); index("contentitem_conversation_hide_time_idx", {conversation_id, hide, time}); unique({content_type, foreign_id}, "IGNORE"); } } public class MessageTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column stanza_id = new Column.Text("stanza_id"); public Column server_id = new Column.Text("server_id") { min_version=10 }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column type_ = new Column.Integer("type"); public Column time = new Column.Long("time"); public Column local_time = new Column.Long("local_time"); public Column body = new Column.Text("body"); public Column encryption = new Column.Integer("encryption"); public Column marked = new Column.Integer("marked"); internal MessageTable(Database db) { base(db, "message"); init({id, stanza_id, server_id, account_id, counterpart_id, our_resource, counterpart_resource, direction, type_, time, local_time, body, encryption, marked}); // get latest messages index("message_account_counterpart_time_idx", {account_id, counterpart_id, time}); // deduplication index("message_account_counterpart_stanzaid_idx", {account_id, counterpart_id, stanza_id}); index("message_account_counterpart_serverid_idx", {account_id, counterpart_id, server_id}); // message by marked index("message_account_marked_idx", {account_id, marked}); fts({body}); } } public class BodyMeta : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id"); public Column from_char = new Column.Integer("from_char"); public Column to_char = new Column.Integer("to_char"); public Column info_type = new Column.Text("info_type"); public Column info = new Column.Text("info"); internal BodyMeta(Database db) { base(db, "body_meta"); init({id, message_id, from_char, to_char, info_type, info}); } } public class MessageCorrectionTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id") { unique=true }; public Column to_stanza_id = new Column.Text("to_stanza_id"); internal MessageCorrectionTable(Database db) { base(db, "message_correction"); init({id, message_id, to_stanza_id}); index("message_correction_to_stanza_id_idx", {to_stanza_id}); } } public class ReplyTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id") { not_null = true, unique=true }; public Column quoted_content_item_id = new Column.Integer("quoted_message_id"); public Column quoted_message_stanza_id = new Column.Text("quoted_message_stanza_id"); public Column quoted_message_from = new Column.Text("quoted_message_from"); internal ReplyTable(Database db) { base(db, "reply"); init({id, message_id, quoted_content_item_id, quoted_message_stanza_id, quoted_message_from}); index("reply_quoted_message_stanza_id", {quoted_message_stanza_id}); } } public class RealJidTable : Table { public Column message_id = new Column.Integer("message_id") { primary_key = true }; public Column real_jid = new Column.Text("real_jid"); internal RealJidTable(Database db) { base(db, "real_jid"); init({message_id, real_jid}); } } public class OccupantIdTable : Table { public Column id = new Column.Integer("id") { primary_key = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column last_nick = new Column.Text("last_nick"); public Column jid_id = new Column.Integer("jid_id"); public Column occupant_id = new Column.Text("occupant_id"); internal OccupantIdTable(Database db) { base(db, "occupant_id"); init({id, account_id, last_nick, jid_id, occupant_id}); unique({account_id, jid_id, occupant_id}, "REPLACE"); } } public class UndecryptedTable : Table { public Column message_id = new Column.Integer("message_id"); public Column type_ = new Column.Integer("type"); public Column data = new Column.Text("data"); internal UndecryptedTable(Database db) { base(db, "undecrypted"); init({message_id, type_, data}); } } public class FileTransferTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column time = new Column.Long("time"); public Column local_time = new Column.Long("local_time"); public Column encryption = new Column.Integer("encryption"); public Column file_name = new Column.Text("file_name"); public Column path = new Column.Text("path"); public Column mime_type = new Column.Text("mime_type"); public Column size = new Column.Integer("size"); public Column state = new Column.Integer("state"); public Column provider = new Column.Integer("provider"); public Column info = new Column.Text("info"); internal FileTransferTable(Database db) { base(db, "file_transfer"); init({id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time, encryption, file_name, path, mime_type, size, state, provider, info}); } } public class CallTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column local_time = new Column.Long("local_time") { not_null = true }; public Column end_time = new Column.Long("end_time"); public Column encryption = new Column.Integer("encryption") { min_version=21 }; public Column state = new Column.Integer("state"); internal CallTable(Database db) { base(db, "call"); init({id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time, end_time, encryption, state}); } } public class CallCounterpartTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column call_id = new Column.Integer("call_id") { not_null = true }; public Column jid_id = new Column.Integer("jid_id") { not_null = true }; public Column resource = new Column.Text("resource"); internal CallCounterpartTable(Database db) { base(db, "call_counterpart"); init({call_id, jid_id, resource}); index("call_counterpart_call_jid_idx", {call_id}); } } public class ConversationTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column jid_id = new Column.Integer("jid_id") { not_null = true }; public Column resource = new Column.Text("resource") { min_version=1 }; public Column active = new Column.BoolInt("active"); public Column active_last_changed = new Column.Integer("active_last_changed") { not_null=true, default="0", min_version=23 }; public Column last_active = new Column.Long("last_active"); public Column type_ = new Column.Integer("type"); public Column encryption = new Column.Integer("encryption"); public Column read_up_to = new Column.Integer("read_up_to"); public Column read_up_to_item = new Column.Integer("read_up_to_item") { not_null=true, default="-1", min_version=15 }; public Column notification = new Column.Integer("notification") { min_version=3 }; public Column send_typing = new Column.Integer("send_typing") { min_version=3 }; public Column send_marker = new Column.Integer("send_marker") { min_version=3 }; public Column pinned = new Column.Integer("pinned") { default="0", min_version=25 }; internal ConversationTable(Database db) { base(db, "conversation"); init({id, account_id, jid_id, resource, active, active_last_changed, last_active, type_, encryption, read_up_to, read_up_to_item, notification, send_typing, send_marker, pinned}); } } public class AvatarTable : Table { public Column jid_id = new Column.Integer("jid_id"); public Column account_id = new Column.Integer("account_id"); public Column hash = new Column.Text("hash"); public Column type_ = new Column.Integer("type"); internal AvatarTable(Database db) { base(db, "contact_avatar"); init({jid_id, account_id, hash, type_}); unique({jid_id, account_id, type_}, "REPLACE"); } } public class EntityIdentityTable : Table { public Column entity = new Column.Text("entity"); public Column category = new Column.Text("category"); public Column type = new Column.Text("type"); public Column entity_name = new Column.Text("name"); internal EntityIdentityTable(Database db) { base(db, "entity_identity"); init({entity, category, entity_name, type}); unique({entity, category, type}, "IGNORE"); index("entity_identity_idx", {entity}); } } public class EntityFeatureTable : Table { public Column entity = new Column.Text("entity"); public Column feature = new Column.Text("feature"); internal EntityFeatureTable(Database db) { base(db, "entity_feature"); init({entity, feature}); unique({entity, feature}, "IGNORE"); index("entity_feature_idx", {entity}); } } public class RosterTable : Table { public Column account_id = new Column.Integer("account_id"); public Column jid = new Column.Text("jid"); public Column handle = new Column.Text("name"); public Column subscription = new Column.Text("subscription"); internal RosterTable(Database db) { base(db, "roster"); init({account_id, jid, handle, subscription}); unique({account_id, jid}, "IGNORE"); } } public class MamCatchupTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column server_jid = new Column.Text("server_jid") { not_null = true }; public Column from_id = new Column.Text("from_id") { not_null = true }; public Column from_time = new Column.Long("from_time") { not_null = true }; public Column from_end = new Column.BoolInt("from_end") { not_null = true }; public Column to_id = new Column.Text("to_id") { not_null = true }; public Column to_time = new Column.Long("to_time") { not_null = true }; internal MamCatchupTable(Database db) { base(db, "mam_catchup"); init({id, account_id, server_jid, from_end, from_id, from_time, to_id, to_time}); } } public class ReactionTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column occupant_id = new Column.Integer("occupant_id"); public Column content_item_id = new Column.Integer("content_item_id") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column jid_id = new Column.Integer("jid_id"); public Column emojis = new Column.Text("emojis"); internal ReactionTable(Database db) { base(db, "reaction"); init({id, account_id, occupant_id, content_item_id, time, jid_id, emojis}); unique({account_id, content_item_id, jid_id}, "REPLACE"); unique({account_id, content_item_id, occupant_id}, "REPLACE"); } } public class SettingsTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column key = new Column.Text("key") { unique = true, not_null = true }; public Column value = new Column.Text("value"); internal SettingsTable(Database db) { base(db, "settings"); init({id, key, value}); } } public class ConversationSettingsTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column conversation_id = new Column.Integer("conversation_id") {not_null=true}; public Column key = new Column.Text("key") { not_null=true }; public Column value = new Column.Text("value"); internal ConversationSettingsTable(Database db) { base(db, "conversation_settings"); init({id, conversation_id, key, value}); index("settings_conversationid_key", { conversation_id, key }, true); } } public AccountTable account { get; private set; } public JidTable jid { get; private set; } public EntityTable entity { get; private set; } public ContentItemTable content_item { get; private set; } public MessageTable message { get; private set; } public BodyMeta body_meta { get; private set; } public ReplyTable reply { get; private set; } public MessageCorrectionTable message_correction { get; private set; } public RealJidTable real_jid { get; private set; } public OccupantIdTable occupantid { get; private set; } public FileTransferTable file_transfer { get; private set; } public CallTable call { get; private set; } public CallCounterpartTable call_counterpart { get; private set; } public ConversationTable conversation { get; private set; } public AvatarTable avatar { get; private set; } public EntityIdentityTable entity_identity { get; private set; } public EntityFeatureTable entity_feature { get; private set; } public RosterTable roster { get; private set; } public MamCatchupTable mam_catchup { get; private set; } public ReactionTable reaction { get; private set; } public SettingsTable settings { get; private set; } public ConversationSettingsTable conversation_settings { get; private set; } public Map jid_table_cache = new HashMap(); public Map jid_table_reverse = new HashMap(Jid.hash_func, Jid.equals_func); public Map account_table_cache = new HashMap(); public Database(string fileName) { base(fileName, VERSION); account = new AccountTable(this); jid = new JidTable(this); entity = new EntityTable(this); content_item = new ContentItemTable(this); message = new MessageTable(this); body_meta = new BodyMeta(this); message_correction = new MessageCorrectionTable(this); reply = new ReplyTable(this); occupantid = new OccupantIdTable(this); real_jid = new RealJidTable(this); file_transfer = new FileTransferTable(this); call = new CallTable(this); call_counterpart = new CallCounterpartTable(this); conversation = new ConversationTable(this); avatar = new AvatarTable(this); entity_identity = new EntityIdentityTable(this); entity_feature = new EntityFeatureTable(this); roster = new RosterTable(this); mam_catchup = new MamCatchupTable(this); reaction = new ReactionTable(this); settings = new SettingsTable(this); conversation_settings = new ConversationSettingsTable(this); init({ account, jid, entity, content_item, message, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, settings, conversation_settings }); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set database properties: %s", e.message); } } public override void migrate(long oldVersion) { // new table columns are added, outdated columns are still present if (oldVersion < 7) { message.fts_rebuild(); } if (oldVersion < 8) { try { exec(""" insert into content_item (conversation_id, time, local_time, content_type, foreign_id, hide) select conversation.id, message.time, message.local_time, 1, message.id, 0 from message join conversation on message.account_id=conversation.account_id and message.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3) where message.body not in (select info from file_transfer where info not null) and message.id not in (select info from file_transfer where info not null) union select conversation.id, message.time, message.local_time, 2, file_transfer.id, 0 from file_transfer join message on file_transfer.info=message.id join conversation on file_transfer.account_id=conversation.account_id and file_transfer.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3)"""); } catch (Error e) { error("Failed to upgrade to database version 8: %s", e.message); } } if (oldVersion < 9) { try { exec(""" insert into content_item (conversation_id, time, local_time, content_type, foreign_id, hide) select conversation.id, message.time, message.local_time, 1, message.id, 1 from message join conversation on message.account_id=conversation.account_id and message.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3) where message.body in (select info from file_transfer where info not null) or message.id in (select info from file_transfer where info not null)"""); } catch (Error e) { error("Failed to upgrade to database version 9: %s", e.message); } } if (oldVersion < 11) { try { exec(""" insert into mam_catchup (account_id, from_end, from_time, to_time) select id, 1, 0, mam_earliest_synced from account where mam_earliest_synced not null and mam_earliest_synced > 0"""); } catch (Error e) { error("Failed to upgrade to database version 11: %s", e.message); } } if (oldVersion < 12) { try { exec("delete from avatar"); } catch (Error e) { error("Failed to upgrade to database version 12: %s", e.message); } } if (oldVersion < 15) { // Initialize `conversation.read_up_to_item` with the content item id corresponding to the `read_up_to` message. try { exec(" update conversation set read_up_to_item=ifnull(( select content_item.id from content_item where content_item.foreign_id=conversation.read_up_to and content_type=1) , -1);"); } catch (Error e) { error("Failed to upgrade to database version 15: %s", e.message); } } if (oldVersion < 16) { try { exec("DROP TABLE contact_avatar"); avatar.create_table_at_version(VERSION); } catch (Error e) { error("Failed to upgrade to database version 16: %s", e.message); } } if (oldVersion < 17) { try { exec("DROP INDEX IF EXISTS contentitem_localtime_counterpart_idx"); exec("CREATE INDEX IF NOT EXISTS contentitem_conversation_hide_localtime_time_idx ON content_item (conversation_id, hide, local_time, time)"); } catch (Error e) { error("Failed to upgrade to database version 17: %s", e.message); } } if (oldVersion < 18) { try { exec("DROP INDEX IF EXISTS contentitem_conversation_hide_localtime_time_idx"); exec("CREATE INDEX IF NOT EXISTS contentitem_conversation_hide_time_idx ON content_item (conversation_id, hide, time)"); exec("DROP INDEX IF EXISTS message_account_counterpart_localtime_idx"); exec("CREATE INDEX IF NOT EXISTS message_account_counterpart_time_idx ON message (account_id, counterpart_id, time)"); exec("DROP INDEX IF EXISTS filetransfer_localtime_counterpart_idx"); } catch (Error e) { error("Failed to upgrade to database version 18: %s", e.message); } } if (oldVersion < 22) { try { exec("INSERT INTO call_counterpart (call_id, jid_id, resource) SELECT id, counterpart_id, counterpart_resource FROM call"); } catch (Error e) { error("Failed to upgrade to database version 22: %s", e.message); } // exec("ALTER TABLE call RENAME TO call2"); // call.create_table_at_version(VERSION); // exec("INSERT INTO call (id, account_id, our_resource, direction, time, local_time, end_time, encryption, state) // SELECT id, account_id, our_resource, direction, time, local_time, end_time, encryption, state // FROM call2"); // exec("DROP TABLE call2"); } if (oldVersion < 23) { try { exec("ALTER TABLE mam_catchup RENAME TO mam_catchup2"); mam_catchup.create_table_at_version(VERSION); exec("""INSERT INTO mam_catchup (id, account_id, server_jid, from_id, from_time, from_end, to_id, to_time) SELECT mam_catchup2.id, account_id, bare_jid, ifnull(from_id, ""), from_time, ifnull(from_end, 0), ifnull(to_id, ""), to_time FROM mam_catchup2 JOIN account ON mam_catchup2.account_id=account.id"""); exec("DROP TABLE mam_catchup2"); } catch (Error e) { error("Failed to upgrade to database version 23 (mam_catchup): %s", e.message); } try { long active_last_updated = (long) new DateTime.now_utc().to_unix(); exec(@"UPDATE conversation SET active_last_changed=$active_last_updated WHERE active_last_changed=0"); } catch (Error e) { error("Failed to upgrade to database version 23 (conversation): %s", e.message); } } } public ArrayList get_accounts() { ArrayList ret = new ArrayList(Account.equals_func); foreach(Row row in account.select()) { try { Account account = new Account.from_row(this, row); ret.add(account); account_table_cache[account.id] = account; } catch (InvalidJidError e) { warning("Ignoring account with invalid Jid: %s", e.message); } } return ret; } public Account? get_account_by_id(int id) { if (account_table_cache.has_key(id)) { return account_table_cache[id]; } else { Row? row = account.row_with(account.id, id).inner; if (row != null) { try { Account a = new Account.from_row(this, row); account_table_cache[a.id] = a; return a; } catch (InvalidJidError e) { warning("Ignoring account with invalid Jid: %s", e.message); } } return null; } } public int add_content_item(Conversation conversation, DateTime time, DateTime local_time, int content_type, int foreign_id, bool hide) { return (int) content_item.insert() .value(content_item.conversation_id, conversation.id) .value(content_item.local_time, (long) local_time.to_unix()) .value(content_item.time, (long) time.to_unix()) .value(content_item.content_type, content_type) .value(content_item.foreign_id, foreign_id) .value(content_item.hide, hide) .perform(); } public Gee.List get_messages(Jid jid, Account account, Message.Type? type, int count, DateTime? before, DateTime? after, int id) { QueryBuilder select = message.select(); if (before != null) { if (id > 0) { select.where(@"time < ? OR (time = ? AND message.id < ?)", { before.to_unix().to_string(), before.to_unix().to_string(), id.to_string() }); } else { select.with(message.id, "<", id); } } if (after != null) { if (id > 0) { select.where(@"time > ? OR (time = ? AND message.id > ?)", { after.to_unix().to_string(), after.to_unix().to_string(), id.to_string() }); } else { select.with(message.time, ">", (long) after.to_unix()); } if (id > 0) { select.with(message.id, ">", id); } } else { select.order_by(message.time, "DESC"); } select.with(message.counterpart_id, "=", get_jid_id(jid)) .with(message.account_id, "=", account.id) .limit(count); if (jid.resourcepart != null) { select.with(message.counterpart_resource, "=", jid.resourcepart); } if (type != null) { select.with(message.type_, "=", (int) type); } select.outer_join_with(real_jid, real_jid.message_id, message.id); select.outer_join_with(message_correction, message_correction.message_id, message.id); LinkedList ret = new LinkedList(); foreach (Row row in select) { try { ret.insert(0, new Message.from_row(this, row)); } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } return ret; } public Message? get_message_by_id(int id) { Row? row = message.row_with(message.id, id).inner; if (row != null) { try { return new Message.from_row(this, row); } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } return null; } public ArrayList get_conversations(Account account) { ArrayList ret = new ArrayList(); foreach (Row row in conversation.select().with(conversation.account_id, "=", account.id)) { try { ret.add(new Conversation.from_row(this, row)); } catch (InvalidJidError e) { warning("Ignoring conversation with invalid Jid: %s", e.message); } } return ret; } public int get_jid_id(Jid jid_obj) { var bare_jid = jid_obj.bare_jid; if (jid_table_reverse.has_key(bare_jid)) { return jid_table_reverse[bare_jid]; } else { Row? row = jid.row_with(jid.bare_jid, jid_obj.bare_jid.to_string()).inner; if (row != null) { int id = row[jid.id]; jid_table_cache[id] = bare_jid; jid_table_reverse[bare_jid] = id; return id; } else { return add_jid(jid_obj); } } } public Jid? get_jid_by_id(int id) throws InvalidJidError { if (jid_table_cache.has_key(id)) { return jid_table_cache[id]; } else { string? bare_jid = jid.select({jid.bare_jid}).with(jid.id, "=", id)[jid.bare_jid]; if (bare_jid != null) { Jid jid_parsed = new Jid(bare_jid); jid_table_cache[id] = jid_parsed; // Only store fully normalized Jids for reverse lookup if (jid_parsed.to_string() == bare_jid) { jid_table_reverse[jid_parsed] = id; } return jid_parsed; } return null; } } private int add_jid(Jid jid_obj) { Jid bare_jid = jid_obj.bare_jid; int id = (int) jid.insert().value(jid.bare_jid, bare_jid.to_string()).perform(); jid_table_cache[id] = bare_jid; jid_table_reverse[bare_jid] = id; return id; } } } dino-0.4.3/libdino/src/service/entity_capabilities_storage.vala0000644000000000000000000000475414452563620023453 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep.ServiceDiscovery; namespace Dino { public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object { private Database db; private HashMap> features_cache = new HashMap>(); private HashMap identity_cache = new HashMap(); public EntityCapabilitiesStorage(Database db) { this.db = db; } public void store_features(string entity, Gee.List features) { if (features_cache.has_key(entity)) return; foreach (string feature in features) { db.entity_feature.insert() .value(db.entity_feature.entity, entity) .value(db.entity_feature.feature, feature) .perform(); } } public void store_identities(string entity, Gee.Set identities) { foreach (Identity identity in identities) { if (identity.category == Identity.CATEGORY_CLIENT) { db.entity_identity.insert() .value(db.entity_identity.entity, entity) .value(db.entity_identity.category, identity.category) .value(db.entity_identity.type, identity.type_) .value(db.entity_identity.entity_name, identity.name) .perform(); return; } } } public Gee.List get_features(string entity) { Gee.List? features = features_cache[entity]; if (features != null) { return features; } features = new ArrayList(); foreach (Row row in db.entity_feature.select({db.entity_feature.feature}).with(db.entity_feature.entity, "=", entity)) { features.add(row[db.entity_feature.feature]); } features_cache[entity] = features; return features; } public Identity? get_identities(string entity) { Identity? identity = identity_cache[entity]; if (identity != null) { return identity; } RowOption row = db.entity_identity.select().with(db.entity_identity.entity, "=", entity).single().row(); if (row.is_present()) { identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]); } identity_cache[entity] = identity; return identity; } } } dino-0.4.3/libdino/src/service/entity_info.vala0000644000000000000000000002262514452563620020226 0ustar rootrootusing Gee; using Dino.Entities; using Qlite; using Xmpp; using Xmpp.Xep; using Xmpp.Xep.ServiceDiscovery; namespace Dino { public class EntityInfo : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("entity_info"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private EntityCapabilitiesStorage entity_capabilities_storage; private HashMap entity_caps_hashes = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap> entity_features = new HashMap>(); private HashMap> jid_features = new HashMap>(Jid.hash_func, Jid.equals_func); private HashMap> entity_identity = new HashMap>(); private HashMap> jid_identity = new HashMap>(Jid.hash_func, Jid.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { EntityInfo m = new EntityInfo(stream_interactor, db); stream_interactor.add_module(m); } private EntityInfo(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.entity_capabilities_storage = new EntityCapabilitiesStorage(db); stream_interactor.account_added.connect(on_account_added); stream_interactor.connection_manager.stream_opened.connect((account, stream) => { stream.received_features_node.connect(() => { string? hash = EntityCapabilities.get_server_caps_hash(stream); if (hash != null) { entity_caps_hashes[account.bare_jid.domain_jid] = hash; } }); }); stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules); remove_old_entities(); Timeout.add_seconds(60 * 60, () => { remove_old_entities(); return true; }); } public async Gee.Set? get_identities(Account account, Jid jid) { if (jid_identity.has_key(jid)) { return jid_identity[jid]; } string? hash = entity_caps_hashes[jid]; if (hash != null) { Gee.Set? identities = get_stored_identities(hash); if (identities != null) return identities; } ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, hash); if (info_result != null) { return info_result.identities; } return null; } public async Identity? get_identity(Account account, Jid jid) { Gee.Set? identities = yield get_identities(account, jid); if (identities == null) return null; foreach (var identity in identities) { if (identity.category == Identity.CATEGORY_CLIENT) { return identity; } } return null; } public async bool has_feature(Account account, Jid jid, string feature) { int has_feature_cached = has_feature_cached_int(account, jid, feature); if (has_feature_cached != -1) { return has_feature_cached == 1; } ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, entity_caps_hashes[jid]); if (info_result == null) return false; return info_result.features.contains(feature); } public bool has_feature_cached(Account account, Jid jid, string feature) { return has_feature_cached_int(account, jid, feature) == 1; } private int has_feature_cached_int(Account account, Jid jid, string feature) { if (jid_features.has_key(jid)) { return jid_features[jid].contains(feature) ? 1 : 0; } string? hash = entity_caps_hashes[jid]; if (hash != null) { Gee.List? features = get_stored_features(hash); if (features != null) { return features.contains(feature) ? 1 : 0; } } return -1; } private void on_received_available_presence(Account account, Presence.Stanza presence) { bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(presence.from.bare_jid, account); if (is_gc) return; string? caps_hash = EntityCapabilities.get_caps_hash(presence); if (caps_hash == null) return; db.entity.upsert() .value(db.entity.account_id, account.id, true) .value(db.entity.jid_id, db.get_jid_id(presence.from), true) .value(db.entity.resource, presence.from.resourcepart, true) .value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix()) .value(db.entity.caps_hash, caps_hash) .perform(); if (caps_hash != null) { entity_caps_hashes[presence.from] = caps_hash; } } private void remove_old_entities() { long timestamp = (long)(new DateTime.now_local().add_days(-14)).to_unix(); db.entity.delete().with(db.entity.last_seen, "<", timestamp).perform(); } private void store_features(string entity, Gee.List features) { if (entity_features.has_key(entity)) return; foreach (string feature in features) { db.entity_feature.insert() .value(db.entity_feature.entity, entity) .value(db.entity_feature.feature, feature) .perform(); } entity_features[entity] = features; } private void store_identities(string entity, Gee.Set identities) { foreach (Identity identity in identities) { db.entity_identity.insert() .value(db.entity_identity.entity, entity) .value(db.entity_identity.category, identity.category) .value(db.entity_identity.type, identity.type_) .value(db.entity_identity.entity_name, identity.name) .perform(); } entity_identity[entity] = identities; } private Gee.List? get_stored_features(string entity) { Gee.List? features = entity_features[entity]; if (features != null) { return features; } features = new ArrayList(); foreach (Row row in db.entity_feature.select({db.entity_feature.feature}).with(db.entity_feature.entity, "=", entity)) { features.add(row[db.entity_feature.feature]); } if (features.size == 0) { return null; } entity_features[entity] = features; return features; } private Gee.Set? get_stored_identities(string entity) { Gee.Set? identities = entity_identity[entity]; if (identities != null) { return identities; } identities = new HashSet(Identity.hash_func, Identity.equals_func); var qry = db.entity_identity.select().with(db.entity_identity.entity, "=", entity); foreach (Row row in qry) { var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]); identities.add(identity); } if (identities.size == 0) { return null; } entity_identity[entity] = identities; return identities; } private async ServiceDiscovery.InfoResult? get_info_result(Account account, Jid jid, string? hash = null) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid); if (info_result == null) return null; if (hash != null && EntityCapabilities.Module.compute_hash_for_info_result(info_result) == hash) { store_features(hash, info_result.features); store_identities(hash, info_result.identities); } else { jid_features[jid] = info_result.features; jid_identity[jid] = info_result.identities; } return info_result; } private void on_account_added(Account account) { var cache = new CapsCacheImpl(account, this); stream_interactor.module_manager.get_module(account, ServiceDiscovery.Module.IDENTITY).cache = cache; stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available.connect((stream, presence) => on_received_available_presence(account, presence)); } private void initialize_modules(Account account, ArrayList modules) { modules.add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); } } public class CapsCacheImpl : CapsCache, Object { private Account account; private EntityInfo entity_info; public CapsCacheImpl(Account account, EntityInfo entity_info) { this.account = account; this.entity_info = entity_info; } public async bool has_entity_feature(Jid jid, string feature) { return yield entity_info.has_feature(account, jid, feature); } public async Gee.Set get_entity_identities(Jid jid) { return yield entity_info.get_identities(account, jid); } } } dino-0.4.3/libdino/src/service/fallback_body.vala0000644000000000000000000000624514452563620020453 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.FallbackBody : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("fallback-body"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private ReceivedMessageListener received_message_listener; public static void start(StreamInteractor stream_interactor, Database db) { FallbackBody m = new FallbackBody(stream_interactor, db); stream_interactor.add_module(m); } private FallbackBody(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.received_message_listener = new ReceivedMessageListener(stream_interactor, db); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "Quote"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; private Database db; public ReceivedMessageListener(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Gee.List fallbacks = Xep.FallbackIndication.get_fallbacks(stanza); if (fallbacks.is_empty) return false; foreach (var fallback in fallbacks) { if (fallback.ns_uri != Xep.Replies.NS_URI) continue; foreach (var location in fallback.locations) { db.body_meta.insert() .value(db.body_meta.message_id, message.id) .value(db.body_meta.info_type, Xep.FallbackIndication.NS_URI) .value(db.body_meta.info, fallback.ns_uri) .value(db.body_meta.from_char, location.from_char) .value(db.body_meta.to_char, location.to_char) .perform(); } message.set_fallbacks(fallbacks); } return false; } } public static string get_quoted_fallback_body(ContentItem content_item) { string fallback = "> "; if (content_item.type_ == MessageItem.TYPE) { Message? quoted_message = ((MessageItem) content_item).message; fallback += Dino.message_body_without_reply_fallback(quoted_message); fallback = fallback.replace("\n", "\n> "); } else if (content_item.type_ == FileItem.TYPE) { FileTransfer? quoted_file = ((FileItem) content_item).file_transfer; fallback += quoted_file.file_name; } fallback += "\n"; return fallback; } }dino-0.4.3/libdino/src/service/file_manager.vala0000644000000000000000000004466514452563620020320 0ustar rootrootusing Gdk; using Gee; using Xmpp; using Dino.Entities; namespace Dino { public class FileManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("file"); public string id { get { return IDENTITY.id; } } public signal void upload_available(Account account); public signal void received_file(FileTransfer file_transfer, Conversation conversation); private StreamInteractor stream_interactor; private Database db; private Gee.List file_senders = new ArrayList(); private Gee.List file_encryptors = new ArrayList(); private Gee.List file_decryptors = new ArrayList(); private Gee.List file_providers = new ArrayList(); public static void start(StreamInteractor stream_interactor, Database db) { FileManager m = new FileManager(stream_interactor, db); stream_interactor.add_module(m); } public static string get_storage_dir() { return Path.build_filename(Dino.get_storage_dir(), "files"); } private FileManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; DirUtils.create_with_parents(get_storage_dir(), 0700); this.add_provider(new JingleFileProvider(stream_interactor)); this.add_sender(new JingleFileSender(stream_interactor)); } public async HashMap get_file_size_limits(Conversation conversation) { HashMap ret = new HashMap(); foreach (FileSender sender in file_senders) { ret[sender.get_id()] = yield sender.get_file_size_limit(conversation); } return ret; } public async void send_file(File file, Conversation conversation) { FileTransfer file_transfer = new FileTransfer(); file_transfer.account = conversation.account; file_transfer.counterpart = conversation.counterpart; if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; } else { file_transfer.ourpart = conversation.account.full_jid; } file_transfer.direction = FileTransfer.DIRECTION_SENT; file_transfer.time = new DateTime.now_utc(); file_transfer.local_time = new DateTime.now_utc(); file_transfer.encryption = conversation.encryption; try { FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE); file_transfer.file_name = file_info.get_display_name(); file_transfer.mime_type = file_info.get_content_type(); file_transfer.size = (int)file_info.get_size(); file_transfer.input_stream = yield file.read_async(); yield save_file(file_transfer); stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); conversation.last_active = file_transfer.time; received_file(file_transfer, conversation); } catch (Error e) { file_transfer.state = FileTransfer.State.FAILED; warning("Error saving outgoing file: %s", e.message); return; } try { var file_meta = new FileMeta(); file_meta.size = file_transfer.size; file_meta.mime_type = file_transfer.mime_type; FileSender file_sender = null; FileEncryptor file_encryptor = null; foreach (FileSender sender in file_senders) { if (yield sender.can_send(conversation, file_transfer)) { if (file_transfer.encryption == Encryption.NONE || yield sender.can_encrypt(conversation, file_transfer)) { file_sender = sender; break; } else { foreach (FileEncryptor encryptor in file_encryptors) { if (encryptor.can_encrypt_file(conversation, file_transfer)) { file_encryptor = encryptor; break; } } if (file_encryptor != null) { file_sender = sender; break; } } } } if (file_sender == null) { throw new FileSendError.UPLOAD_FAILED("No sender/encryptor combination available"); } if (file_encryptor != null) { file_meta = file_encryptor.encrypt_file(conversation, file_transfer); } FileSendData file_send_data = yield file_sender.prepare_send_file(conversation, file_transfer, file_meta); if (file_encryptor != null) { file_send_data = file_encryptor.preprocess_send_file(conversation, file_transfer, file_send_data, file_meta); } yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta); } catch (Error e) { warning("Send file error: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } } public async void download_file(FileTransfer file_transfer) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); FileProvider? file_provider = null; foreach (FileProvider fp in file_providers) { if (file_transfer.provider == fp.get_id()) { file_provider = fp; } } yield download_file_internal(file_provider, file_transfer, conversation); } public async bool is_upload_available(Conversation? conversation) { if (conversation == null) return false; foreach (FileSender file_sender in file_senders) { if (yield file_sender.is_upload_available(conversation)) return true; } return false; } public void add_provider(FileProvider file_provider) { file_providers.add(file_provider); file_provider.file_incoming.connect((info, from, time, local_time, conversation, receive_data, file_meta) => { handle_incoming_file.begin(file_provider, info, from, time, local_time, conversation, receive_data, file_meta); }); } public void add_sender(FileSender file_sender) { file_senders.add(file_sender); file_sender.upload_available.connect((account) => { upload_available(account); }); file_senders.sort((a, b) => { return (int) (b.get_priority() - a.get_priority()); }); } public void add_file_encryptor(FileEncryptor encryptor) { file_encryptors.add(encryptor); } public void add_file_decryptor(FileDecryptor decryptor) { file_decryptors.add(decryptor); } public bool is_sender_trustworthy(FileTransfer file_transfer, Conversation conversation) { if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return true; Jid relevant_jid = conversation.counterpart; if (conversation.type_ == Conversation.Type.GROUPCHAT) { relevant_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(file_transfer.from, conversation.account); } if (relevant_jid == null) return false; bool in_roster = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, relevant_jid) != null; return in_roster; } private async FileMeta get_file_meta(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation, FileReceiveData receive_data_) throws FileReceiveError { FileReceiveData receive_data = receive_data_; FileMeta file_meta = file_provider.get_file_meta(file_transfer); if (file_meta.size == -1) { foreach (FileDecryptor file_decryptor in file_decryptors) { if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data); break; } } file_meta = yield file_provider.get_meta_info(file_transfer, receive_data, file_meta); file_transfer.size = (int)file_meta.size; file_transfer.file_name = file_meta.file_name; file_transfer.mime_type = file_meta.mime_type; } return file_meta; } private async void download_file_internal(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) { try { // Get meta info FileReceiveData receive_data = file_provider.get_file_receive_data(file_transfer); FileDecryptor? file_decryptor = null; foreach (FileDecryptor decryptor in file_decryptors) { if (decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { file_decryptor = decryptor; break; } } if (file_decryptor != null) { receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data); } FileMeta file_meta = yield get_file_meta(file_provider, file_transfer, conversation, receive_data); InputStream? input_stream = null; // Download and decrypt file file_transfer.state = FileTransfer.State.IN_PROGRESS; if (file_decryptor != null) { file_meta = file_decryptor.prepare_download_file(conversation, file_transfer, receive_data, file_meta); } input_stream = yield file_provider.download(file_transfer, receive_data, file_meta); if (file_decryptor != null) { input_stream = yield file_decryptor.decrypt_file(input_stream, conversation, file_transfer, receive_data); } // Save file string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name; File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); uint8[] buffer = new uint8[1024]; ssize_t read; while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) { buffer.length = (int) read; yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable); buffer.length = 1024; } yield input_stream.close_async(Priority.LOW, file_transfer.cancellable); yield os.close_async(Priority.LOW, file_transfer.cancellable); file_transfer.path = file.get_basename(); file_transfer.input_stream = yield file.read_async(); FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE); file_transfer.mime_type = file_info.get_content_type(); file_transfer.state = FileTransfer.State.COMPLETE; } catch (Error e) { warning("Error downloading file: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } } private async void handle_incoming_file(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) { FileTransfer file_transfer = new FileTransfer(); file_transfer.account = conversation.account; file_transfer.counterpart = file_transfer.direction == FileTransfer.DIRECTION_RECEIVED ? from : conversation.counterpart; if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; file_transfer.direction = from.equals(file_transfer.ourpart) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED; } else { file_transfer.ourpart = conversation.account.full_jid; file_transfer.direction = from.equals_bare(file_transfer.ourpart) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED; } file_transfer.time = time; file_transfer.local_time = local_time; file_transfer.provider = file_provider.get_id(); file_transfer.file_name = file_meta.file_name; file_transfer.size = (int)file_meta.size; file_transfer.info = info; var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta); if (encryption != Encryption.NONE) file_transfer.encryption = encryption; foreach (FileDecryptor decryptor in file_decryptors) { if (decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { file_transfer.encryption = decryptor.get_encryption(); } } stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); if (is_sender_trustworthy(file_transfer, conversation)) { try { yield get_file_meta(file_provider, file_transfer, conversation, receive_data); } catch (Error e) { warning("Error downloading file: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } if (file_transfer.size >= 0 && file_transfer.size < 5000000) { download_file_internal.begin(file_provider, file_transfer, conversation, (_, res) => { download_file_internal.end(res); }); } } conversation.last_active = file_transfer.time; received_file(file_transfer, conversation); } private async void save_file(FileTransfer file_transfer) throws FileSendError { try { string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name; File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET); file_transfer.state = FileTransfer.State.COMPLETE; file_transfer.path = filename; file_transfer.input_stream = yield file.read_async(); } catch (Error e) { throw new FileSendError.SAVE_FAILED("Saving file error: %s".printf(e.message)); } } } public errordomain FileSendError { ENCRYPTION_FAILED, UPLOAD_FAILED, SAVE_FAILED } public errordomain FileReceiveError { GET_METADATA_FAILED, DECRYPTION_FAILED, DOWNLOAD_FAILED } public class FileMeta { public int64 size = -1; public string? mime_type = null; public string? file_name = null; public Encryption encryption = Encryption.NONE; } public class HttpFileMeta : FileMeta { public Message message; } public class FileSendData { } public class HttpFileSendData : FileSendData { public string url_down { get; set; } public string url_up { get; set; } public HashMap headers { get; set; } public bool encrypt_message { get; set; default=true; } } public class FileReceiveData { } public class HttpFileReceiveData : FileReceiveData { public string url { get; set; } } public interface FileProvider : Object { public signal void file_incoming(string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta); public abstract Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta); public abstract FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError; public abstract FileReceiveData? get_file_receive_data(FileTransfer file_transfer); public abstract async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError; public abstract async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError; public abstract int get_id(); } public interface FileSender : Object { public signal void upload_available(Account account); public abstract async bool is_upload_available(Conversation conversation); public abstract async long get_file_size_limit(Conversation conversation); public abstract async bool can_send(Conversation conversation, FileTransfer file_transfer); public abstract async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError; public abstract async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError; public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer); public abstract int get_id(); public abstract float get_priority(); } public interface FileEncryptor : Object { public abstract bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer); public abstract FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError; public abstract FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError; } public interface FileDecryptor : Object { public abstract Encryption get_encryption(); public abstract FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data); public abstract FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta); public abstract bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data); public abstract async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError; } } dino-0.4.3/libdino/src/service/file_transfer_storage.vala0000644000000000000000000000450414452563620022242 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class FileTransferStorage : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("file_store"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap files_by_db_id = new WeakMap(); public static void start(StreamInteractor stream_interactor, Database db) { FileTransferStorage m = new FileTransferStorage(stream_interactor, db); stream_interactor.add_module(m); } private FileTransferStorage(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_file(FileTransfer file_transfer) { file_transfer.persist(db); cache_file(file_transfer); } public FileTransfer? get_file_by_id(int id, Conversation conversation) { FileTransfer? file_transfer = files_by_db_id[id]; if (file_transfer != null) { return file_transfer; } RowOption row_option = db.file_transfer.select().with(db.file_transfer.id, "=", id).row(); return create_file_from_row_opt(row_option, conversation); } private FileTransfer? create_file_from_row_opt(RowOption row_opt, Conversation conversation) { if (!row_opt.is_present()) return null; try { FileTransfer file_transfer = new FileTransfer.from_row(db, row_opt.inner, FileManager.get_storage_dir()); if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = conversation.counterpart.with_resource(file_transfer.ourpart.resourcepart); } cache_file(file_transfer); return file_transfer; } catch (InvalidJidError e) { warning("Got file transfer with invalid Jid: %s", e.message); } return null; } private void cache_file(FileTransfer file_transfer) { files_by_db_id[file_transfer.id] = file_transfer; } } }dino-0.4.3/libdino/src/service/history_sync.vala0000644000000000000000000007276614452563620020447 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; public class Dino.HistorySync { private StreamInteractor stream_interactor; private Database db; public HashMap> current_catchup_id = new HashMap>(Account.hash_func, Account.equals_func); public WeakMap sync_streams = new WeakMap(Account.hash_func, Account.equals_func); public HashMap> cancellables = new HashMap>(Account.hash_func, Account.equals_func); public HashMap> mam_times = new HashMap>(); public HashMap hitted_range = new HashMap(); // Server ID of the latest message of the previous segment public HashMap catchup_until_id = new HashMap(Account.hash_func, Account.equals_func); // Time of the latest message of the previous segment public HashMap catchup_until_time = new HashMap(Account.hash_func, Account.equals_func); private HashMap> stanzas = new HashMap>(); public class HistorySync(Database db, StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect((account, stream) => { if (current_catchup_id.has_key(account)) { debug("MAM: [%s] Reset catchup_id", account.bare_jid.to_string()); current_catchup_id[account].clear(); } }); } public bool process(Account account, Xmpp.MessageStanza message_stanza) { var mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message_stanza); if (mam_flag != null) { process_mam_message(account, message_stanza, mam_flag); return true; } else { update_latest_db_range(account, message_stanza); return false; } } public void update_latest_db_range(Account account, Xmpp.MessageStanza message_stanza) { Jid mam_server = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(message_stanza.from.bare_jid, account) ? message_stanza.from.bare_jid : account.bare_jid; if (!current_catchup_id.has_key(account) || !current_catchup_id[account].has_key(mam_server)) return; string? stanza_id = UniqueStableStanzaIDs.get_stanza_id(message_stanza, mam_server); if (stanza_id == null) return; db.mam_catchup.update() .with(db.mam_catchup.id, "=", current_catchup_id[account][mam_server]) .set(db.mam_catchup.to_time, (long)new DateTime.now_utc().to_unix()) .set(db.mam_catchup.to_id, stanza_id) .perform(); } public void process_mam_message(Account account, Xmpp.MessageStanza message_stanza, Xmpp.MessageArchiveManagement.MessageFlag mam_flag) { Jid mam_server = mam_flag.sender_jid; Jid message_author = message_stanza.from; // MUC servers may only send MAM messages from that MUC bool is_muc_mam = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(mam_server, account) && message_author.equals_bare(mam_server); bool from_our_server = mam_server.equals_bare(account.bare_jid); if (!is_muc_mam && !from_our_server) { warning("Received alleged MAM message from %s, ignoring", mam_server.to_string()); return; } if (!stanzas.has_key(mam_flag.query_id)) stanzas[mam_flag.query_id] = new ArrayList(); stanzas[mam_flag.query_id].add(message_stanza); } private void on_unprocessed_message(Account account, XmppStream stream, MessageStanza message) { // Check that it's a legit MAM server bool is_muc_mam = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(message.from, account); bool from_our_server = message.from.equals_bare(account.bare_jid); if (!is_muc_mam && !from_our_server) return; // Get the server time of the message and store it in `mam_times` string? id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", "id"); if (id == null) return; StanzaNode? delay_node = message.stanza.get_deep_subnode(Xmpp.MessageArchiveManagement.NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); if (delay_node == null) { warning("MAM result did not contain delayed time %s", message.stanza.to_string()); return; } DateTime? time = DelayedDelivery.get_time_for_node(delay_node); if (time == null) return; mam_times[account][id] = time; // Check if this is the target message string? query_id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", Xmpp.MessageArchiveManagement.NS_URI + ":queryid"); if (query_id != null && id == catchup_until_id[account]) { debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id); hitted_range[query_id] = -2; } } public void on_server_id_duplicate(Account account, Xmpp.MessageStanza message_stanza, Entities.Message message) { Xmpp.MessageArchiveManagement.MessageFlag? mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message_stanza); if (mam_flag == null) return; // debug(@"MAM: [%s] Hitted range duplicate server id. id %s qid %s", account.bare_jid.to_string(), message.server_id, mam_flag.query_id); if (catchup_until_time.has_key(account) && mam_flag.server_time.compare(catchup_until_time[account]) < 0) { hitted_range[mam_flag.query_id] = -1; // debug(@"MAM: [%s] In range (time) %s < %s", account.bare_jid.to_string(), mam_flag.server_time.to_string(), catchup_until_time[account].to_string()); } } public async void fetch_everything(Account account, Jid mam_server, Cancellable? cancellable = null, DateTime until_earliest_time = new DateTime.from_unix_utc(0)) { debug("Fetch everything for %s %s", mam_server.to_string(), until_earliest_time != null ? @"(until $until_earliest_time)" : ""); RowOption latest_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); Row? latest_row = latest_row_opt.is_present() ? latest_row_opt.inner : null; Row? new_row = yield fetch_latest_page(account, mam_server, latest_row, until_earliest_time, cancellable); if (new_row != null) { current_catchup_id[account][mam_server] = new_row[db.mam_catchup.id]; } else if (latest_row != null) { current_catchup_id[account][mam_server] = latest_row[db.mam_catchup.id]; } // Set the previous and current row Row? previous_row = null; Row? current_row = null; if (new_row != null) { current_row = new_row; previous_row = latest_row; } else if (latest_row != null) { current_row = latest_row; RowOption previous_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, "<", current_row[db.mam_catchup.from_time]) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); previous_row = previous_row_opt.is_present() ? previous_row_opt.inner : null; } // Fetch messages between two db ranges and merge them while (current_row != null && previous_row != null) { if (current_row[db.mam_catchup.from_end]) return; debug("[%s] Fetching between ranges %s - %s", mam_server.to_string(), previous_row[db.mam_catchup.to_time].to_string(), current_row[db.mam_catchup.from_time].to_string()); current_row = yield fetch_between_ranges(account, mam_server, previous_row, current_row, cancellable); if (current_row == null) return; RowOption previous_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, "<", current_row[db.mam_catchup.from_time]) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); previous_row = previous_row_opt.is_present() ? previous_row_opt.inner : null; } // We're at the earliest range. Try to expand it even further back. if (current_row == null || current_row[db.mam_catchup.from_end]) return; // We don't want to fetch before the earliest range over and over again in MUCs if it's after until_earliest_time. // For now, don't query if we are within a week of until_earliest_time if (until_earliest_time != null && current_row[db.mam_catchup.from_time] > until_earliest_time.add(-TimeSpan.DAY * 7).to_unix()) return; yield fetch_before_range(account, mam_server, current_row, until_earliest_time); } // Fetches the latest page (up to previous db row). Extends the previous db row if it was reached, creates a new row otherwise. public async Row? fetch_latest_page(Account account, Jid mam_server, Row? latest_row, DateTime? until_earliest_time, Cancellable? cancellable = null) { debug("[%s | %s] Fetching latest page", account.bare_jid.to_string(), mam_server.to_string()); int latest_row_id = -1; DateTime latest_message_time = until_earliest_time; string? latest_message_id = null; if (latest_row != null) { latest_row_id = latest_row[db.mam_catchup.id]; latest_message_time = (new DateTime.from_unix_utc(latest_row[db.mam_catchup.to_time])).add_minutes(-5); latest_message_id = latest_row[db.mam_catchup.to_id]; // Make sure we only fetch to until_earliest_time if latest_message_time is further back if (until_earliest_time != null && latest_message_time.compare(until_earliest_time) < 0) { latest_message_time = until_earliest_time.add_minutes(-5); latest_message_id = null; } } var query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_latest(mam_server, latest_message_time, latest_message_id); PageRequestResult page_result = yield get_mam_page(account, query_params, null, cancellable); debug("[%s | %s] Latest page result: %s", account.bare_jid.to_string(), mam_server.to_string(), page_result.page_result.to_string()); if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled) { return null; } // Catchup finished within first page. Update latest db entry. if (latest_row_id != -1 && page_result.page_result in new PageResult[] { PageResult.TargetReached, PageResult.NoMoreMessages }) { if (page_result.stanzas == null) return null; string latest_mam_id = page_result.query_result.last; long latest_mam_time = (long) mam_times[account][latest_mam_id].to_unix(); var query = db.mam_catchup.update() .with(db.mam_catchup.id, "=", latest_row_id) .set(db.mam_catchup.to_time, latest_mam_time) .set(db.mam_catchup.to_id, latest_mam_id); if (page_result.page_result == PageResult.NoMoreMessages) { // If the server doesn't have more messages, store that this range is at its end. query.set(db.mam_catchup.from_end, true); } query.perform(); return null; } if (page_result.query_result.first == null || page_result.query_result.last == null) { return null; } // Either we need to fetch more pages or this is the first db entry ever debug("[%s | %s] Creating new db range for latest page", account.bare_jid.to_string(), mam_server.to_string()); string from_id = page_result.query_result.first; string to_id = page_result.query_result.last; if (!mam_times[account].has_key(from_id) || !mam_times[account].has_key(to_id)) { debug("Missing from/to id %s %s", from_id, to_id); return null; } long from_time = (long) mam_times[account][from_id].to_unix(); long to_time = (long) mam_times[account][to_id].to_unix(); int new_row_id = (int) db.mam_catchup.insert() .value(db.mam_catchup.account_id, account.id) .value(db.mam_catchup.server_jid, mam_server.to_string()) .value(db.mam_catchup.from_id, from_id) .value(db.mam_catchup.from_time, from_time) .value(db.mam_catchup.from_end, page_result.page_result == PageResult.NoMoreMessages) .value(db.mam_catchup.to_id, to_id) .value(db.mam_catchup.to_time, to_time) .perform(); return db.mam_catchup.select().with(db.mam_catchup.id, "=", new_row_id).single().row().inner; } /** Fetches messages between the end of `earlier_range` and start of `later_range` ** Merges the `earlier_range` db row into the `later_range` db row. ** @return The resulting range comprising `earlier_range`, `later_rage`, and everything in between. null if fetching/merge failed. **/ private async Row? fetch_between_ranges(Account account, Jid mam_server, Row earlier_range, Row later_range, Cancellable? cancellable = null) { int later_range_id = (int) later_range[db.mam_catchup.id]; DateTime earliest_time = new DateTime.from_unix_utc(earlier_range[db.mam_catchup.to_time]); DateTime latest_time = new DateTime.from_unix_utc(later_range[db.mam_catchup.from_time]); debug("[%s | %s] Fetching between %s (%s) and %s (%s)", account.bare_jid.to_string(), mam_server.to_string(), earliest_time.to_string(), earlier_range[db.mam_catchup.to_id], latest_time.to_string(), later_range[db.mam_catchup.from_id]); var query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_between(mam_server, earliest_time, earlier_range[db.mam_catchup.to_id], latest_time, later_range[db.mam_catchup.from_id]); PageRequestResult page_result = yield fetch_query(account, query_params, later_range_id, cancellable); if (page_result.page_result == PageResult.TargetReached || page_result.page_result == PageResult.NoMoreMessages) { debug("[%s | %s] Merging range %i into %i", account.bare_jid.to_string(), mam_server.to_string(), earlier_range[db.mam_catchup.id], later_range_id); // Merge earlier range into later one. db.mam_catchup.update() .with(db.mam_catchup.id, "=", later_range_id) .set(db.mam_catchup.from_time, earlier_range[db.mam_catchup.from_time]) .set(db.mam_catchup.from_id, earlier_range[db.mam_catchup.from_id]) .set(db.mam_catchup.from_end, earlier_range[db.mam_catchup.from_end]) .perform(); db.mam_catchup.delete().with(db.mam_catchup.id, "=", earlier_range[db.mam_catchup.id]).perform(); // Return the updated version of the later range return db.mam_catchup.select().with(db.mam_catchup.id, "=", later_range_id).single().row().inner; } return null; } private async void fetch_before_range(Account account, Jid mam_server, Row range, DateTime? until_earliest_time, Cancellable? cancellable = null) { DateTime latest_time = new DateTime.from_unix_utc(range[db.mam_catchup.from_time]); string latest_id = range[db.mam_catchup.from_id]; debug("[%s | %s] Fetching before range < %s, %s", account.bare_jid.to_string(), mam_server.to_string(), latest_time.to_string(), latest_id); Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params; if (until_earliest_time == null) { query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_before(mam_server, latest_time, latest_id); } else { query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_between( mam_server, until_earliest_time, null, latest_time, latest_id ); } yield fetch_query(account, query_params, range[db.mam_catchup.id], cancellable); } /** * Iteratively fetches all pages returned for a query (until a PageResult other than MorePagesAvailable is returned) * @return The last PageRequestResult result **/ private async PageRequestResult fetch_query(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, int db_id, Cancellable? cancellable = null) { debug("[%s | %s] Fetch query %s - %s", account.bare_jid.to_string(), query_params.mam_server.to_string(), query_params.start != null ? query_params.start.to_string() : "", query_params.end != null ? query_params.end.to_string() : ""); PageRequestResult? page_result = null; do { page_result = yield get_mam_page(account, query_params, page_result, cancellable); debug("[%s | %s] Page result %s (got stanzas: %s)", account.bare_jid.to_string(), query_params.mam_server.to_string(), page_result.page_result.to_string(), (page_result.stanzas != null).to_string()); if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled || page_result.query_result.first == null) return page_result; string earliest_mam_id = page_result.query_result.first; long earliest_mam_time = (long)mam_times[account][earliest_mam_id].to_unix(); debug("Updating %s to %s, %s", query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id); var query = db.mam_catchup.update() .with(db.mam_catchup.id, "=", db_id) .set(db.mam_catchup.from_time, earliest_mam_time) .set(db.mam_catchup.from_id, earliest_mam_id); if (page_result.page_result == PageResult.NoMoreMessages) { // If the server doesn't have more messages, store that this range is at its end. query.set(db.mam_catchup.from_end, true); } query.perform(); } while (page_result.page_result == PageResult.MorePagesAvailable); return page_result; } enum PageResult { MorePagesAvailable, TargetReached, NoMoreMessages, Error, Cancelled } /** * prev_page_result: null if this is the first page request **/ private async PageRequestResult get_mam_page(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, PageRequestResult? prev_page_result, Cancellable? cancellable = null) { XmppStream stream = stream_interactor.get_stream(account); Xmpp.MessageArchiveManagement.QueryResult query_result = null; if (prev_page_result == null) { query_result = yield Xmpp.MessageArchiveManagement.V2.query_archive(stream, query_params, cancellable); } else { query_result = yield Xmpp.MessageArchiveManagement.V2.page_through_results(stream, query_params, prev_page_result.query_result, cancellable); } return yield process_query_result(account, query_params, query_result, cancellable); } private async PageRequestResult process_query_result(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, Xmpp.MessageArchiveManagement.QueryResult query_result, Cancellable? cancellable = null) { PageResult page_result = PageResult.MorePagesAvailable; if (query_result.malformed || query_result.error) { page_result = PageResult.Error; } // We wait until all the messages from the page are processed (and we got the `mam_times` from them) Idle.add(process_query_result.callback, Priority.LOW); yield; // We might have successfully reached the target or the server doesn't have all messages stored anymore // If it's the former, we'll overwrite the value with PageResult.MorePagesAvailable below. if (query_result.complete) { page_result = PageResult.NoMoreMessages; } string selection = null; string[] selection_args = {}; string query_id = query_params.query_id; string? after_id = query_params.start_id; var stanzas_for_query = stanzas.has_key(query_id) && !stanzas[query_id].is_empty ? stanzas[query_id] : null; if (cancellable != null && cancellable.is_cancelled()) { stanzas.unset(query_id); return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } if (stanzas_for_query != null) { // Check it we reached our target (from_id) foreach (Xmpp.MessageStanza message in stanzas_for_query) { Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); if (mam_message_flag != null && mam_message_flag.mam_id != null) { if (after_id != null && mam_message_flag.mam_id == after_id) { // Successfully fetched the whole range yield send_messages_back_into_pipeline(account, query_id, cancellable); if (cancellable != null && cancellable.is_cancelled()) { return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } return new PageRequestResult(PageResult.TargetReached, query_result, stanzas_for_query); } } } if (hitted_range.has_key(query_id) && hitted_range[query_id] == -2) { // Message got filtered out by xmpp-vala, but succesful range fetch nevertheless yield send_messages_back_into_pipeline(account, query_id); if (cancellable != null && cancellable.is_cancelled()) { return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } return new PageRequestResult(PageResult.TargetReached, query_result, stanzas_for_query); } } yield send_messages_back_into_pipeline(account, query_id); if (cancellable != null && cancellable.is_cancelled()) { page_result = PageResult.Cancelled; } return new PageRequestResult(page_result, query_result, stanzas_for_query); } private async void send_messages_back_into_pipeline(Account account, string query_id, Cancellable? cancellable = null) { if (!stanzas.has_key(query_id)) return; foreach (Xmpp.MessageStanza message in stanzas[query_id]) { if (cancellable != null && cancellable.is_cancelled()) break; yield stream_interactor.get_module(MessageProcessor.IDENTITY).run_pipeline_announce(account, message); } stanzas.unset(query_id); } private void on_account_added(Account account) { cleanup_db_ranges(db, account); mam_times[account] = new HashMap(); stream_interactor.connection_manager.stream_attached_modules.connect((account, stream) => { if (!current_catchup_id.has_key(account)) { current_catchup_id[account] = new HashMap(Jid.hash_func, Jid.equals_func); } else { current_catchup_id[account].clear(); } }); stream_interactor.module_manager.get_module(account, Xmpp.MessageArchiveManagement.Module.IDENTITY).feature_available.connect((stream) => { consider_fetch_everything(account, stream); }); stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_message_unprocessed.connect((stream, message) => { on_unprocessed_message(account, stream, message); }); } private void consider_fetch_everything(Account account, XmppStream stream) { if (sync_streams.has(account, stream)) return; debug("[%s] MAM available", account.bare_jid.to_string()); sync_streams[account] = stream; if (!cancellables.has_key(account)) { cancellables[account] = new HashMap(); } if (cancellables[account].has_key(account.bare_jid)) { cancellables[account][account.bare_jid].cancel(); } cancellables[account][account.bare_jid] = new Cancellable(); fetch_everything.begin(account, account.bare_jid, cancellables[account][account.bare_jid], new DateTime.from_unix_utc(0), (_, res) => { fetch_everything.end(res); cancellables[account].unset(account.bare_jid); }); } public static void cleanup_db_ranges(Database db, Account account) { var ranges = new HashMap>(Jid.hash_func, Jid.equals_func); foreach (Row row in db.mam_catchup.select().with(db.mam_catchup.account_id, "=", account.id)) { var mam_range = new MamRange(); mam_range.id = row[db.mam_catchup.id]; mam_range.server_jid = new Jid(row[db.mam_catchup.server_jid]); mam_range.from_time = row[db.mam_catchup.from_time]; mam_range.from_id = row[db.mam_catchup.from_id]; mam_range.from_end = row[db.mam_catchup.from_end]; mam_range.to_time = row[db.mam_catchup.to_time]; mam_range.to_id = row[db.mam_catchup.to_id]; if (!ranges.has_key(mam_range.server_jid)) ranges[mam_range.server_jid] = new ArrayList(); ranges[mam_range.server_jid].add(mam_range); } var to_delete = new ArrayList(); foreach (Jid server_jid in ranges.keys) { foreach (var range1 in ranges[server_jid]) { if (to_delete.contains(range1)) continue; foreach (MamRange range2 in ranges[server_jid]) { debug("[%s | %s] | %s - %s vs %s - %s", account.bare_jid.to_string(), server_jid.to_string(), range1.from_time.to_string(), range1.to_time.to_string(), range2.from_time.to_string(), range2.to_time.to_string()); if (range1 == range2 || to_delete.contains(range2)) continue; // Check if range2 is a subset of range1 // range1: ##################### // range2: ###### if (range1.from_time <= range2.from_time && range1.to_time >= range2.to_time) { warning("Removing db range which is a subset of %li-%li", range1.from_time, range1.to_time); to_delete.add(range2); continue; } // Check if range2 is an extension of range1 (towards earlier) // range1: ##################### // range2: ############### if (range1.from_time <= range2.to_time <= range1.to_time && range2.from_time <= range1.from_time) { warning("Removing db range that overlapped %li-%li (towards earlier)", range1.from_time, range1.to_time); db.mam_catchup.update() .with(db.mam_catchup.id, "=", range1.id) .set(db.mam_catchup.from_id, range2.from_id) .set(db.mam_catchup.from_time, range2.from_time) .set(db.mam_catchup.from_end, range2.from_end) .perform(); to_delete.add(range2); continue; } } } } foreach (MamRange row in to_delete) { db.mam_catchup.delete().with(db.mam_catchup.id, "=", row.id).perform(); warning("Removing db range %s %li-%li", row.server_jid.to_string(), row.from_time, row.to_time); } } class MamRange { public int id; public Jid server_jid; public long from_time; public string from_id; public bool from_end; public long to_time; public string to_id; } class PageRequestResult { public Gee.List stanzas { get; set; } public PageResult page_result { get; set; } public Xmpp.MessageArchiveManagement.QueryResult query_result { get; set; } public PageRequestResult(PageResult page_result, Xmpp.MessageArchiveManagement.QueryResult query_result, Gee.List? stanzas) { this.page_result = page_result; this.query_result = query_result; this.stanzas = stanzas; } } }dino-0.4.3/libdino/src/service/jingle_file_transfers.vala0000644000000000000000000002421014452563620022225 0ustar rootrootusing Gdk; using Gee; using Xmpp; using Dino.Entities; namespace Dino { public interface JingleFileEncryptionHelper : Object { public abstract bool can_transfer(Conversation conversation); public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid = null); public abstract string? get_precondition_name(Conversation conversation, FileTransfer file_transfer); public abstract Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer); public abstract Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer); } public class JingleFileEncryptionHelperTransferOnly : JingleFileEncryptionHelper, Object { public bool can_transfer(Conversation conversation) { return true; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { return false; } public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) { return null; } public Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer) { return null; } public Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer) { return Encryption.NONE; } } public class JingleFileHelperRegistry { private static JingleFileHelperRegistry INSTANCE; public static JingleFileHelperRegistry instance { get { if (INSTANCE == null) { INSTANCE = new JingleFileHelperRegistry(); INSTANCE.add_encryption_helper(Encryption.NONE, new JingleFileEncryptionHelperTransferOnly()); } return INSTANCE; } } internal HashMap encryption_helpers = new HashMap(); public void add_encryption_helper(Encryption encryption, JingleFileEncryptionHelper helper) { encryption_helpers[encryption] = helper; } public JingleFileEncryptionHelper? get_encryption_helper(Encryption encryption) { if (encryption_helpers.has_key(encryption)) { return encryption_helpers[encryption]; } return null; } } public class JingleFileProvider : FileProvider, Object { private StreamInteractor stream_interactor; private HashMap file_transfers = new HashMap(); public JingleFileProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); } public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError { var file_meta = new FileMeta(); file_meta.file_name = file_transfer.file_name; file_meta.size = file_transfer.size; return file_meta; } public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) { return new FileReceiveData(); } public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { return file_meta; } public Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info]; if (jingle_file_transfer == null) { warning("Could not determine jingle encryption - transfer data not available anymore"); return Encryption.NONE; } foreach (JingleFileEncryptionHelper helper in JingleFileHelperRegistry.instance.encryption_helpers.values) { var encryption = helper.get_encryption(jingle_file_transfer); if (encryption != Encryption.NONE) return encryption; } return Encryption.NONE; } public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { // TODO(hrxi) What should happen if `stream == null`? XmppStream? stream = stream_interactor.get_stream(file_transfer.account); Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info]; if (jingle_file_transfer == null) { throw new FileReceiveError.DOWNLOAD_FAILED("Transfer data not available anymore"); } try { yield jingle_file_transfer.accept(stream); } catch (IOError e) { throw new FileReceiveError.DOWNLOAD_FAILED("Establishing connection did not work"); } return jingle_file_transfer.stream; } public int get_id() { return 1; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xmpp.Xep.JingleFileTransfer.Module.IDENTITY).file_incoming.connect((stream, jingle_file_transfer) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jingle_file_transfer.peer.bare_jid, account); if (conversation == null) return; string id = random_uuid(); file_transfers[id] = jingle_file_transfer; FileMeta file_meta = new FileMeta(); file_meta.size = jingle_file_transfer.size; file_meta.file_name = jingle_file_transfer.file_name; var time = new DateTime.now_utc(); var from = jingle_file_transfer.peer.bare_jid; file_incoming(id, from, time, time, conversation, new FileReceiveData(), file_meta); }); } } public class JingleFileSender : FileSender, Object { private StreamInteractor stream_interactor; public JingleFileSender(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public async bool is_upload_available(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(conversation.encryption); if (helper == null) return false; if (!helper.can_transfer(conversation)) return false; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; Gee.List? resources = stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart); if (resources == null) return false; foreach (Jid full_jid in resources) { if (yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { return true; } } return false; } public async long get_file_size_limit(Conversation conversation) { if (yield can_send_conv(conversation)) { return int.MAX; } return -1; } public async bool can_send(Conversation conversation, FileTransfer file_transfer) { return yield can_send_conv(conversation); } private async bool can_send_conv(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; // No file specific restrictions apply to Jingle file transfers return yield is_upload_available(conversation); } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); if (helper == null) return false; return yield helper.can_encrypt(conversation, file_transfer); } public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError { if (file_meta is HttpFileMeta) { throw new FileSendError.UPLOAD_FAILED("Cannot upload http file meta over Jingle"); } return new FileSendData(); } public async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError { XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream available"); JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); bool must_encrypt = helper != null && yield helper.can_encrypt(conversation, file_transfer); // TODO(hrxi): Prioritization of transports (and resources?). foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) { if (full_jid.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid)) { continue; } if (!yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { continue; } if (must_encrypt && !yield helper.can_encrypt(conversation, file_transfer, full_jid)) { continue; } string? precondition_name = null; Object? precondition_options = null; if (must_encrypt) { precondition_name = helper.get_precondition_name(conversation, file_transfer); precondition_options = helper.get_precondition_options(conversation, file_transfer); if (precondition_name == null) { throw new FileSendError.ENCRYPTION_FAILED("Should have created a precondition, but did not"); } } try { yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream(stream, full_jid, file_transfer.input_stream, file_transfer.server_file_name, file_meta.size, precondition_name, precondition_options); } catch (Error e) { throw new FileSendError.UPLOAD_FAILED(@"offer_file_stream failed: $(e.message)"); } return; } } public int get_id() { return 1; } public float get_priority() { return 50; } } } dino-0.4.3/libdino/src/service/message_correction.vala0000644000000000000000000002200714452563620021544 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; namespace Dino { public class MessageCorrection : StreamInteractionModule, MessageListener { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_correction"); public string id { get { return IDENTITY.id; } } public signal void received_correction(ContentItem content_item); private StreamInteractor stream_interactor; private Database db; private HashMap> last_messages = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap outstanding_correction_nodes = new HashMap(); public static void start(StreamInteractor stream_interactor, Database db) { MessageCorrection m = new MessageCorrection(stream_interactor, db); stream_interactor.add_module(m); } public MessageCorrection(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(this); stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(check_add_correction_node); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect((jid, account) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, account, Conversation.Type.GROUPCHAT); if (conversation != null) { if (last_messages.has_key(conversation)) last_messages[conversation].unset(jid); } }); } public void send_correction(Conversation conversation, Message old_message, string correction_text) { string stanza_id = old_message.edit_to ?? old_message.stanza_id; Message out_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(correction_text, conversation); out_message.edit_to = stanza_id; out_message.quoted_item_id = old_message.quoted_item_id; outstanding_correction_nodes[out_message.stanza_id] = stanza_id; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(out_message, conversation); db.message_correction.insert() .value(db.message_correction.message_id, out_message.id) .value(db.message_correction.to_stanza_id, stanza_id) .perform(); db.content_item.update() .with(db.content_item.foreign_id, "=", old_message.id) .with(db.content_item.content_type, "=", 1) .set(db.content_item.foreign_id, out_message.id) .perform(); on_received_correction(conversation, out_message.id); } public bool is_own_correction_allowed(Conversation conversation, Message message) { string stanza_id = message.edit_to ?? message.stanza_id; Jid? own_jid = null; if (conversation.type_ == Conversation.Type.CHAT) { own_jid = conversation.account.full_jid; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); } if (own_jid == null) return false; return last_messages.has_key(conversation) && last_messages[conversation].has_key(own_jid) && last_messages[conversation][own_jid].stanza_id == stanza_id; } private void check_add_correction_node(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (outstanding_correction_nodes.has_key(message.stanza_id)) { LastMessageCorrection.set_replace_id(message_stanza, outstanding_correction_nodes[message.stanza_id]); outstanding_correction_nodes.unset(message.stanza_id); } else { if (!last_messages.has_key(conversation)) { last_messages[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } last_messages[conversation][message.from] = message; } } public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY" }; public override string action_group { get { return "CORRECTION"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) { // Don't process messages or corrections from MUC history or MUC MAM DateTime? mam_delay = Xep.DelayedDelivery.get_time_for_message(stanza, message.from.bare_jid); if (mam_delay != null) return false; if (Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null) return false; } string? replace_id = Xep.LastMessageCorrection.get_replace_id(stanza); if (replace_id == null) { if (!last_messages.has_key(conversation)) { last_messages[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } last_messages[conversation][message.from] = message; return false; } if (!last_messages.has_key(conversation) || !last_messages[conversation].has_key(message.from)) return false; Message original_message = last_messages[conversation][message.from]; if (original_message.stanza_id != replace_id) return false; int message_id_to_be_updated = get_latest_correction_message_id(conversation.account.id, replace_id, db.get_jid_id(message.counterpart), message.counterpart.resourcepart); if (message_id_to_be_updated == -1) { message_id_to_be_updated = original_message.id; } db.message_correction.insert() .value(db.message_correction.message_id, message.id) .value(db.message_correction.to_stanza_id, replace_id) .perform(); int current_correction_message_id = get_latest_correction_message_id(conversation.account.id, replace_id, db.get_jid_id(message.counterpart), message.counterpart.resourcepart); if (current_correction_message_id != message_id_to_be_updated) { db.content_item.update() .with(db.content_item.foreign_id, "=", message_id_to_be_updated) .with(db.content_item.content_type, "=", 1) .set(db.content_item.foreign_id, current_correction_message_id) .perform(); message.edit_to = replace_id; on_received_correction(conversation, current_correction_message_id); return true; } return false; } private void on_received_correction(Conversation conversation, int message_id) { ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message_id); if (content_item != null) { received_correction(content_item); } } private int get_latest_correction_message_id(int account_id, string stanza_id, int counterpart_jid_id, string? counterpart_resource) { var qry = db.message_correction.select({db.message.id}) .join_with(db.message, db.message.id, db.message_correction.message_id) .with(db.message.account_id, "=", account_id) .with(db.message.counterpart_id, "=", counterpart_jid_id) .with(db.message_correction.to_stanza_id, "=", stanza_id) .order_by(db.message.time, "DESC"); if (counterpart_resource != null) { qry.with(db.message.counterpart_resource, "=", counterpart_resource); } RowOption row = qry.single().row(); if (row.is_present()) { return row[db.message.id]; } return -1; } private void on_account_added(Account account) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); foreach (Conversation conversation in conversations) { if (conversation.type_ != Conversation.Type.CHAT) continue; HashMap last_conversation_messages = new HashMap(Jid.hash_func, Jid.equals_func); Gee.List messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation); for (int i = messages.size - 1; i > 0; i--) { Message message = messages[i]; if (!last_conversation_messages.has_key(message.from) && message.edit_to == null) { last_conversation_messages[message.from] = message; } } last_messages[conversation] = last_conversation_messages; } } } } dino-0.4.3/libdino/src/service/message_processor.vala0000644000000000000000000006373314452563620021427 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; namespace Dino { public class MessageProcessor : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_processor"); public string id { get { return IDENTITY.id; } } public signal void message_received(Entities.Message message, Conversation conversation); public signal void build_message_stanza(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation); public signal void pre_message_send(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation); public signal void message_sent(Entities.Message message, Conversation conversation); public signal void message_sent_or_received(Entities.Message message, Conversation conversation); public signal void history_synced(Account account); public HistorySync history_sync; public MessageListenerHolder received_pipeline = new MessageListenerHolder(); private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { MessageProcessor m = new MessageProcessor(stream_interactor, db); stream_interactor.add_module(m); } private MessageProcessor(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.history_sync = new HistorySync(db, stream_interactor); received_pipeline.connect(new DeduplicateMessageListener(this)); received_pipeline.connect(new FilterMessageListener()); received_pipeline.connect(new StoreMessageListener(this, stream_interactor)); received_pipeline.connect(new StoreContentItemListener(stream_interactor)); received_pipeline.connect(new MamMessageListener(stream_interactor)); stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect(send_unsent_chat_messages); stream_interactor.stream_resumed.connect(send_unsent_chat_messages); } public Entities.Message send_text(string text, Conversation conversation) { Entities.Message message = create_out_message(text, conversation); return send_message(message, conversation); } public Entities.Message send_message(Entities.Message message, Conversation conversation) { stream_interactor.get_module(ContentItemStore.IDENTITY).insert_message(message, conversation); send_xmpp_message(message, conversation); message_sent(message, conversation); return message; } private void convert_sending_to_unsent_msgs(Account account) { db.message.update() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", Message.Marked.SENDING) .set(db.message.marked, Message.Marked.UNSENT) .perform(); } private void send_unsent_chat_messages(Account account) { var select = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", (int) Message.Marked.UNSENT) .with(db.message.type_, "=", (int) Message.Type.CHAT); send_unsent_messages(account, select); } public void send_unsent_muc_messages(Account account, Jid muc_jid) { var select = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", (int) Message.Marked.UNSENT) .with(db.message.counterpart_id, "=", db.get_jid_id(muc_jid)); send_unsent_messages(account, select); } private void send_unsent_messages(Account account, QueryBuilder select) { foreach (Row row in select) { try { Message message = new Message.from_row(db, row); Conversation? msg_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message.counterpart, account, Util.get_conversation_type_for_message(message)); if (msg_conv != null) { Message cached_msg = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message.id, msg_conv); send_xmpp_message(cached_msg ?? message, msg_conv, true); } } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_message.connect( (stream, message) => { on_message_received.begin(account, message); }); stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_error.connect((stream, message_stanza, error_stanza) => { Message? message = null; Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversations(message_stanza.from, account); foreach (Conversation conversation in conversations) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_stanza.id, conversation); if (message != null) break; } if (message == null) return; // We don't care about delivery errors if our counterpart already ACKed the message. if (message.marked in Message.MARKED_RECEIVED) return; warning("Message delivery error from %s. Type: %s, Condition: %s, Text: %s", message_stanza.from.to_string(), error_stanza.type_ ?? "-", error_stanza.condition, error_stanza.text ?? "-"); if (error_stanza.condition == Xmpp.ErrorStanza.CONDITION_RECIPIENT_UNAVAILABLE && error_stanza.type_ == Xmpp.ErrorStanza.TYPE_CANCEL) return; message.marked = Message.Marked.ERROR; }); convert_sending_to_unsent_msgs(account); } private async void on_message_received(Account account, Xmpp.MessageStanza message_stanza) { // If it's a message from MAM, it's going to be processed by HistorySync which calls run_pipeline_announce later. if (history_sync.process(account, message_stanza)) return; run_pipeline_announce.begin(account, message_stanza); } public async void run_pipeline_announce(Account account, Xmpp.MessageStanza message_stanza) { Entities.Message message = yield parse_message_stanza(account, message_stanza); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(message); if (conversation == null) return; bool abort = yield received_pipeline.run(message, message_stanza, conversation); if (abort) return; if (message.direction == Entities.Message.DIRECTION_RECEIVED) { message_received(message, conversation); } else if (message.direction == Entities.Message.DIRECTION_SENT) { message_sent(message, conversation); } message_sent_or_received(message, conversation); } public async Entities.Message parse_message_stanza(Account account, Xmpp.MessageStanza message) { string? body = message.body; if (body != null) body = body.strip(); Entities.Message new_message = new Entities.Message(body); new_message.account = account; new_message.stanza_id = Xep.UniqueStableStanzaIDs.get_origin_id(message) ?? message.id; Jid? counterpart_override = null; if (message.from.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(message.from.bare_jid, account))) { new_message.direction = Entities.Message.DIRECTION_SENT; counterpart_override = message.from.bare_jid; } else if (account.bare_jid.equals_bare(message.from)) { new_message.direction = Entities.Message.DIRECTION_SENT; } else { new_message.direction = Entities.Message.DIRECTION_RECEIVED; } new_message.counterpart = counterpart_override ?? (new_message.direction == Entities.Message.DIRECTION_SENT ? message.to : message.from); new_message.ourpart = new_message.direction == Entities.Message.DIRECTION_SENT ? message.from : message.to; XmppStream? stream = stream_interactor.get_stream(account); Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); if (mam_message_flag != null && mam_message_flag.mam_id != null) { bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI); if (server_does_mam) { new_message.server_id = mam_message_flag.mam_id; } } else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid); } } else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid); } } if (mam_message_flag != null) new_message.local_time = mam_message_flag.server_time; DateTime now = new DateTime.from_unix_utc(new DateTime.now_utc().to_unix()); // Remove milliseconds. They are not stored in the db and might lead to ordering issues when compared with times from the db. if (new_message.local_time == null || new_message.local_time.compare(now) > 0) new_message.local_time = now; Xep.DelayedDelivery.MessageFlag? delayed_message_flag = Xep.DelayedDelivery.MessageFlag.get_flag(message); if (delayed_message_flag != null) new_message.time = delayed_message_flag.datetime; if (new_message.time == null || new_message.time.compare(new_message.local_time) > 0) new_message.time = new_message.local_time; new_message.type_ = yield determine_message_type(account, message, new_message); return new_message; } private async Entities.Message.Type determine_message_type(Account account, Xmpp.MessageStanza message_stanza, Entities.Message message) { if (message_stanza.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { return Entities.Message.Type.GROUPCHAT; } if (message_stanza.type_ == Xmpp.MessageStanza.TYPE_CHAT) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message.counterpart.bare_jid, account); if (conversation != null) { if (conversation.type_ == Conversation.Type.CHAT) { return Entities.Message.Type.CHAT; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { return Entities.Message.Type.GROUPCHAT_PM; } } else { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { Gee.Set? identities = yield stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY).get_entity_identities(stream, message.counterpart.bare_jid); if (identities == null) { return Entities.Message.Type.CHAT; } foreach (Xep.ServiceDiscovery.Identity identity in identities) { if (identity.category == Xep.ServiceDiscovery.Identity.CATEGORY_CONFERENCE) { return Entities.Message.Type.GROUPCHAT_PM; } else { return Entities.Message.Type.CHAT; } } } } } return Entities.Message.Type.CHAT; } private bool is_duplicate(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Account account = conversation.account; // Deduplicate by server_id if (message.server_id != null) { QueryBuilder builder = db.message.select() .with(db.message.server_id, "=", message.server_id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.account_id, "=", account.id); // If the message is a duplicate if (builder.count() > 0) { history_sync.on_server_id_duplicate(account, stanza, message); return true; } } // Deduplicate messages by uuid bool is_uuid = message.stanza_id != null && Regex.match_simple("""[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}""", message.stanza_id); if (is_uuid) { QueryBuilder builder = db.message.select() .with(db.message.stanza_id, "=", message.stanza_id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.account_id, "=", account.id); if (message.direction == Message.DIRECTION_RECEIVED) { if (message.counterpart.resourcepart != null) { builder.with(db.message.counterpart_resource, "=", message.counterpart.resourcepart); } else { builder.with_null(db.message.counterpart_resource); } } else if (message.direction == Message.DIRECTION_SENT) { if (message.ourpart.resourcepart != null) { builder.with(db.message.our_resource, "=", message.ourpart.resourcepart); } else { builder.with_null(db.message.our_resource); } } bool duplicate = builder.single().row().is_present(); return duplicate; } // Deduplicate messages based on content and metadata QueryBuilder builder = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.body, "=", message.body) .with(db.message.time, "<", (long) message.time.add_minutes(1).to_unix()) .with(db.message.time, ">", (long) message.time.add_minutes(-1).to_unix()); if (message.stanza_id != null) { builder.with(db.message.stanza_id, "=", message.stanza_id); } else { builder.with_null(db.message.stanza_id); } if (message.counterpart.resourcepart != null) { builder.with(db.message.counterpart_resource, "=", message.counterpart.resourcepart); } else { builder.with_null(db.message.counterpart_resource); } return builder.count() > 0; } private class DeduplicateMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "FILTER_EMPTY", "MUC" }; public override string action_group { get { return "DEDUPLICATE"; } } public override string[] after_actions { get { return after_actions_const; } } private MessageProcessor outer; public DeduplicateMessageListener(MessageProcessor outer) { this.outer = outer; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { return outer.is_duplicate(message, stanza, conversation); } } private class FilterMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DECRYPT" }; public override string action_group { get { return "FILTER_EMPTY"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { return (message.body == null); } } private class StoreMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY" }; public override string action_group { get { return "STORE"; } } public override string[] after_actions { get { return after_actions_const; } } private MessageProcessor outer; private StreamInteractor stream_interactor; public StoreMessageListener(MessageProcessor outer, StreamInteractor stream_interactor) { this.outer = outer; this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (message.body == null || outer.is_duplicate(message, stanza, conversation)) return true; stream_interactor.get_module(MessageStorage.IDENTITY).add_message(message, conversation); return false; } } private class StoreContentItemListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY", "STORE", "CORRECTION", "MESSAGE_REINTERPRETING" }; public override string action_group { get { return "STORE_CONTENT_ITEM"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public StoreContentItemListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (message.body == null) return true; stream_interactor.get_module(ContentItemStore.IDENTITY).insert_message(message, conversation); return false; } } private class MamMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE" }; public override string action_group { get { return "MAM_NODE"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public MamMessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null; XmppStream? stream = stream_interactor.get_stream(conversation.account); Xmpp.MessageArchiveManagement.Flag mam_flag = Xmpp.MessageArchiveManagement.Flag.get_flag(stream); if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) { conversation.account.mam_earliest_synced = message.local_time; } return false; } } public Entities.Message create_out_message(string text, Conversation conversation) { Entities.Message message = new Entities.Message(text); message.type_ = Util.get_message_type_for_conversation(conversation); message.stanza_id = random_uuid(); message.account = conversation.account; message.body = text; DateTime now = new DateTime.from_unix_utc(new DateTime.now_utc().to_unix()); // Remove milliseconds. They are not stored in the db and might lead to ordering issues when compared with times from the db. message.time = now; message.local_time = now; message.direction = Entities.Message.DIRECTION_SENT; message.counterpart = conversation.counterpart; if (conversation.type_ == Conversation.Type.GROUPCHAT) { message.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; message.real_jid = conversation.account.bare_jid; } else { message.ourpart = conversation.account.full_jid; } message.marked = Entities.Message.Marked.UNSENT; message.encryption = conversation.encryption; stream_interactor.get_module(MessageStorage.IDENTITY).add_message(message, conversation); return message; } public void send_xmpp_message(Entities.Message message, Conversation conversation, bool delayed = false) { XmppStream stream = stream_interactor.get_stream(conversation.account); message.marked = Entities.Message.Marked.SENDING; if (stream == null) { message.marked = Entities.Message.Marked.UNSENT; return; } MessageStanza new_message = new MessageStanza(message.stanza_id); new_message.to = message.counterpart; new_message.body = message.body; if (conversation.type_ == Conversation.Type.GROUPCHAT) { new_message.type_ = MessageStanza.TYPE_GROUPCHAT; } else { new_message.type_ = MessageStanza.TYPE_CHAT; } string? fallback = get_fallback_body_set_infos(message, new_message, conversation); new_message.body = fallback == null ? message.body : fallback + message.body; build_message_stanza(message, new_message, conversation); pre_message_send(message, new_message, conversation); if (message.marked == Entities.Message.Marked.UNSENT || message.marked == Entities.Message.Marked.WONTSEND) return; if (delayed) { DelayedDelivery.Module.set_message_delay(new_message, message.time); } // Set an origin ID if a MUC doen't guarantee to keep IDs if (conversation.type_ == Conversation.Type.GROUPCHAT) { Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY); if (flag == null) { message.marked = Entities.Message.Marked.UNSENT; return; } if(!flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.STABLE_ID)) { UniqueStableStanzaIDs.set_origin_id(new_message, message.stanza_id); } } if (conversation.get_send_typing_setting(stream_interactor) == Conversation.Setting.ON) { ChatStateNotifications.add_state_to_message(new_message, ChatStateNotifications.STATE_ACTIVE); } stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, new_message, (_, res) => { try { stream.get_module(MessageModule.IDENTITY).send_message.end(res); if (message.marked == Message.Marked.SENDING) { message.marked = Message.Marked.SENT; } // The server might not have given us the resource we asked for. In that case, store the actual resource the message was sent with. Relevant for deduplication. Jid? current_own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid; if (!conversation.type_.is_muc_semantic() && current_own_jid != null && !current_own_jid.equals(message.ourpart)) { message.ourpart = current_own_jid; } } catch (IOError e) { message.marked = Entities.Message.Marked.UNSENT; if (stream != stream_interactor.get_stream(conversation.account)) { Timeout.add_seconds(3, () => { send_unsent_chat_messages(conversation.account); return false; }); } } }); } public string? get_fallback_body_set_infos(Entities.Message message, MessageStanza new_stanza, Conversation conversation) { if (message.quoted_item_id == 0) return null; ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id); if (content_item == null) return null; Jid? quoted_sender = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_sender_for_content_item(conversation, content_item); string? quoted_stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (quoted_sender != null && quoted_stanza_id != null) { Xep.Replies.set_reply_to(new_stanza, new Xep.Replies.ReplyTo(quoted_sender, quoted_stanza_id)); } string fallback = FallbackBody.get_quoted_fallback_body(content_item); var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback.char_count()); Xep.FallbackIndication.set_fallback(new_stanza, new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location })); return fallback; } } public abstract class MessageListener : Xmpp.OrderedListener { public abstract async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation); } public class MessageListenerHolder : Xmpp.ListenerHolder { public async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { foreach (OrderedListener ol in listeners) { MessageListener l = ol as MessageListener; bool stop = yield l.run(message, stanza, conversation); if (stop) return true; } return false; } } } dino-0.4.3/libdino/src/service/message_storage.vala0000644000000000000000000002045414452563620021045 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class MessageStorage : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_cache"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap messages_by_db_id = new WeakMap(); private HashMap> messages_by_stanza_id = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap> messages_by_server_id = new HashMap>(Conversation.hash_func, Conversation.equals_func); // This is to keep the last 300 messages such that we don't have to recreate the newest ones all the time private LinkedList message_refs = new LinkedList(); public static void start(StreamInteractor stream_interactor, Database db) { MessageStorage m = new MessageStorage(stream_interactor, db); stream_interactor.add_module(m); } private MessageStorage(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_message(Message message, Conversation conversation) { message.persist(db); cache_message(message, conversation); } public Gee.List get_messages(Conversation conversation, int count = 50) { var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id) .limit(count); Gee.List ret = new LinkedList(Message.equals_func); foreach (Row row in query) { Message? message = messages_by_db_id[row[db.message.id]]; if (message == null) { message = create_message_from_row(row, conversation); } ret.insert(0, message); } return ret; } public Message? get_last_message(Conversation conversation) { Gee.List messages = get_messages(conversation, 1); if (messages.size > 0) { return messages[0]; } return null; } public Gee.List get_messages_before_message(Conversation? conversation, DateTime before, int id, int count = 20) { Gee.List db_messages = db.get_messages(conversation.counterpart, conversation.account, Util.get_message_type_for_conversation(conversation), count, before, null, id); Gee.List ret = new ArrayList(); foreach (Message message in db_messages) { ret.add(new MessageItem(message, conversation, -1)); } return ret; } public Gee.List get_messages_after_message(Conversation? conversation, DateTime after, int id, int count = 20) { Gee.List db_messages = db.get_messages(conversation.counterpart, conversation.account, Util.get_message_type_for_conversation(conversation), count, null, after, id); Gee.List ret = new ArrayList(); foreach (Message message in db_messages) { ret.add(new MessageItem(message, conversation, -1)); } return ret; } public Message? get_message_by_id(int id, Conversation conversation) { Message? message = messages_by_db_id[id]; if (message != null) { return message; } RowOption row_option = db.message.select().with(db.message.id, "=", id) .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id) .row(); return create_message_from_row_opt(row_option, conversation); } public Message? get_message_by_stanza_id(string stanza_id, Conversation conversation) { if (messages_by_stanza_id.has_key(conversation)) { Message? message = messages_by_stanza_id[conversation][stanza_id]; if (message != null) { return message; } } var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .with(db.message.stanza_id, "=", stanza_id) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id); if (conversation.counterpart.resourcepart != null) { query.with(db.message.counterpart_resource, "=", conversation.counterpart.resourcepart); } RowOption row_option = query.single().row(); return create_message_from_row_opt(row_option, conversation); } public Message? get_message_by_server_id(string server_id, Conversation conversation) { if (messages_by_server_id.has_key(conversation)) { Message? message = messages_by_server_id[conversation][server_id]; if (message != null) { return message; } } var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .with(db.message.server_id, "=", server_id) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id); if (conversation.counterpart.resourcepart == null) { query.with_null(db.message.counterpart_resource); } else { query.with(db.message.counterpart_resource, "=", conversation.counterpart.resourcepart); } RowOption row_option = query.single().row(); return create_message_from_row_opt(row_option, conversation); } private Message? create_message_from_row_opt(RowOption row_option, Conversation conversation) { if (!row_option.is_present()) return null; return create_message_from_row(row_option.inner, conversation); } private Message? create_message_from_row(Row row, Conversation conversation) { try { Message message = new Message.from_row(db, row); cache_message(message, conversation); return message; } catch (InvalidJidError e) { warning("Got message with invalid Jid: %s", e.message); } return null; } private void cache_message(Message message, Conversation conversation) { messages_by_db_id[message.id] = message; if (message.stanza_id != null) { if (!messages_by_stanza_id.has_key(conversation)) { messages_by_stanza_id[conversation] = new WeakMap(); } messages_by_stanza_id[conversation][message.stanza_id] = message; } if (message.server_id != null) { if (!messages_by_server_id.has_key(conversation)) { messages_by_server_id[conversation] = new WeakMap(); } messages_by_server_id[conversation][message.server_id] = message; } message_refs.insert(0, message); if (message_refs.size > 300) { message_refs.remove_at(message_refs.size - 1); } } } } dino-0.4.3/libdino/src/service/module_manager.vala0000644000000000000000000001121214452563620020644 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public class ModuleManager { private HashMap> module_map = new HashMap>(Account.hash_func, Account.equals_func); public signal void initialize_account_modules(Account account, ArrayList modules); public T? get_module(Account account, Xmpp.ModuleIdentity identity) { if (identity == null) return null; lock (module_map) { if (!module_map.has_key(account)) { initialize(account); } var res = module_map[account].filter((module) => identity.matches(module)); if (res != null && res.next()) { return identity.cast(res.get()); } } return null; } public ArrayList get_modules(Account account, string? resource = null) { ArrayList modules = new ArrayList(); lock (module_map) { if (!module_map.has_key(account)) initialize(account); foreach (XmppStreamModule module in module_map[account]) modules.add(module); } foreach (XmppStreamModule module in module_map[account]) { if (module.get_id() == Bind.Module.IDENTITY.id) { ((Bind.Module) module).requested_resource = resource ?? account.resourcepart; } else if (module.get_id() == Sasl.Module.IDENTITY.id) { ((Sasl.Module) module).password = account.password; } } return modules; } public void initialize(Account account) { lock(module_map) { module_map[account] = new ArrayList(); module_map[account].add(new Iq.Module()); module_map[account].add(new Sasl.Module(account.bare_jid.to_string(), account.password)); module_map[account].add(new Xep.StreamManagement.Module()); module_map[account].add(new Bind.Module(account.resourcepart)); module_map[account].add(new Session.Module()); module_map[account].add(new Roster.Module()); module_map[account].add(new Xep.ServiceDiscovery.Module.with_identity("client", "pc", "Dino")); module_map[account].add(new Xep.PrivateXmlStorage.Module()); module_map[account].add(new Xep.Bookmarks.Module()); module_map[account].add(new Xep.Bookmarks2.Module()); module_map[account].add(new Presence.Module()); module_map[account].add(new Xmpp.MessageModule()); module_map[account].add(new Xmpp.MessageArchiveManagement.Module()); module_map[account].add(new Xep.MessageCarbons.Module()); module_map[account].add(new Xep.Muc.Module()); module_map[account].add(new Xep.Pubsub.Module()); module_map[account].add(new Xep.MessageDeliveryReceipts.Module()); module_map[account].add(new Xep.BlockingCommand.Module()); module_map[account].add(new Xep.ChatStateNotifications.Module()); module_map[account].add(new Xep.ChatMarkers.Module()); module_map[account].add(new Xep.Ping.Module()); module_map[account].add(new Xep.DelayedDelivery.Module()); module_map[account].add(new StreamError.Module()); module_map[account].add(new Xep.InBandRegistration.Module()); module_map[account].add(new Xep.HttpFileUpload.Module()); module_map[account].add(new Xep.Reactions.Module()); module_map[account].add(new Xep.Socks5Bytestreams.Module()); module_map[account].add(new Xep.InBandBytestreams.Module()); module_map[account].add(new Xep.Jingle.Module()); module_map[account].add(new Xep.JingleSocks5Bytestreams.Module()); module_map[account].add(new Xep.JingleInBandBytestreams.Module()); module_map[account].add(new Xep.JingleFileTransfer.Module()); module_map[account].add(new Xep.Jet.Module()); module_map[account].add(new Xep.LastMessageCorrection.Module()); module_map[account].add(new Xep.DirectMucInvitations.Module()); module_map[account].add(new Xep.JingleMessageInitiation.Module()); module_map[account].add(new Xep.OccupantIds.Module()); module_map[account].add(new Xep.JingleRawUdp.Module()); module_map[account].add(new Xep.Muji.Module()); module_map[account].add(new Xep.CallInvites.Module()); module_map[account].add(new Xep.Coin.Module()); initialize_account_modules(account, module_map[account]); } } } } dino-0.4.3/libdino/src/service/muc_manager.vala0000644000000000000000000010220114452563620020142 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; namespace Dino { public class MucManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("muc_manager"); public string id { get { return IDENTITY.id; } } public signal void left(Account account, Jid jid); public signal void subject_set(Account account, Jid jid, string? subject); public signal void room_info_updated(Account account, Jid muc_jid); public signal void private_room_occupant_updated(Account account, Jid room, Jid occupant); public signal void invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason); public signal void voice_request_received(Account account, Jid room_jid, Jid from_jid, string nick); public signal void received_occupant_role(Account account, Jid jid, Xep.Muc.Role? role); public signal void bookmarks_updated(Account account, Set conferences); public signal void conference_added(Account account, Conference conference); public signal void conference_removed(Account account, Jid jid); private StreamInteractor stream_interactor; private HashMap> mucs_todo = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> mucs_joining = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> mucs_sync_cancellables = new HashMap>(Account.hash_func, Account.equals_func); private HashMap enter_errors = new HashMap(Jid.hash_func, Jid.equals_func); private ReceivedMessageListener received_message_listener; private HashMap bookmarks_provider = new HashMap(Account.hash_func, Account.equals_func); private HashMap> invites = new HashMap>(Account.hash_func, Account.equals_func); public HashMap default_muc_server = new HashMap(Account.hash_func, Account.equals_func); private HashMap> own_occupant_ids = new HashMap>(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor) { MucManager m = new MucManager(stream_interactor); stream_interactor.add_module(m); } private MucManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.received_message_listener = new ReceivedMessageListener(stream_interactor); stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect((conversation) => { if (conversation.type_ == Conversation.Type.GROUPCHAT) { part(conversation.account, conversation.counterpart); } }); stream_interactor.stream_resumed.connect((account, stream) => self_ping(account)); Timeout.add_seconds(60 * 3, () => { foreach (Account account in stream_interactor.get_accounts()) { self_ping(account); } return true; }); } // already_autojoin: Without this flag we'd be retrieving bookmarks (to check for autojoin) from the sender on every join public async Muc.JoinResult? join(Account account, Jid jid, string? nick, string? password, bool already_autojoin = false, Cancellable? cancellable = null) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; string nick_ = (nick ?? account.localpart) ?? account.domainpart; DateTime? history_since = null; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account); if (conversation != null) { Entities.Message? last_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(conversation); if (last_message != null) history_since = last_message.time; } bool receive_history = true; EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI); if (can_do_mam) { receive_history = false; history_since = null; } if (!mucs_joining.has_key(account)) { mucs_joining[account] = new HashSet(Jid.hash_bare_func, Jid.equals_bare_func); } mucs_joining[account].add(jid); if (!mucs_todo.has_key(account)) { mucs_todo[account] = new HashSet(Jid.hash_bare_func, Jid.equals_bare_func); } mucs_todo[account].add(jid.with_resource(nick_)); Muc.JoinResult? res = yield stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid, nick_, password, history_since, receive_history, null); mucs_joining[account].remove(jid); if (res.nick != null) { // Join completed enter_errors.unset(jid); if (!already_autojoin) set_autojoin(account, stream, jid, nick, password); stream_interactor.get_module(MessageProcessor.IDENTITY).send_unsent_muc_messages(account, jid); Conversation joined_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.GROUPCHAT); joined_conversation.nickname = nick; stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(joined_conversation); if (can_do_mam) { var history_sync = stream_interactor.get_module(MessageProcessor.IDENTITY).history_sync; if (conversation == null) { // We never joined the conversation before, just fetch the latest MAM page yield history_sync.fetch_latest_page(account, jid.bare_jid, null, new DateTime.from_unix_utc(0), cancellable); } else { // Fetch everything up to the last time the user actively joined if (!mucs_sync_cancellables.has_key(account)) { mucs_sync_cancellables[account] = new HashMap(); } if (!mucs_sync_cancellables[account].has_key(jid.bare_jid)) { mucs_sync_cancellables[account][jid.bare_jid] = new Cancellable(); history_sync.fetch_everything.begin(account, jid.bare_jid, mucs_sync_cancellables[account][jid.bare_jid], conversation.active_last_changed, (_, res) => { history_sync.fetch_everything.end(res); mucs_sync_cancellables[account].unset(jid.bare_jid); }); } } } } else if (res.muc_error != null) { // Join failed enter_errors[jid] = res.muc_error; } return res; } public void part(Account account, Jid jid) { if (!mucs_todo.has_key(account) || !mucs_todo[account].contains(jid)) return; mucs_todo[account].remove(jid); XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; unset_autojoin(account, stream, jid); stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, jid.bare_jid); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account); if (conversation != null) stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); cancel_sync(account, jid); } private void cancel_sync(Account account, Jid jid) { if (mucs_sync_cancellables.has_key(account) && mucs_sync_cancellables[account].has_key(jid.bare_jid) && !mucs_sync_cancellables[account][jid.bare_jid].is_cancelled()) { mucs_sync_cancellables[account][jid.bare_jid].cancel(); } } public async DataForms.DataForm? get_config_form(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; return yield stream.get_module(Xep.Muc.Module.IDENTITY).get_config_form(stream, jid); } public async void set_config_form(Account account, Jid jid, DataForms.DataForm data_form) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; yield stream.get_module(Xep.Muc.Module.IDENTITY).set_config_form(stream, jid, data_form); } public void change_subject(Account account, Jid jid, string subject) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_subject(stream, jid.bare_jid, subject); } public async void change_nick(Conversation conversation, string new_nick) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; // Check if this would be a valid nick try { conversation.counterpart.with_resource(new_nick); } catch (InvalidJidError error) { return; } stream.get_module(Xep.Muc.Module.IDENTITY).change_nick(stream, conversation.counterpart, new_nick); conversation.nickname = new_nick; if (mucs_todo.has_key(conversation.account)) { mucs_todo[conversation.account].remove(conversation.counterpart); mucs_todo[conversation.account].add(conversation.counterpart.with_resource(new_nick)); } // Update nick in bookmark Set? conferences = yield bookmarks_provider[conversation.account].get_conferences(stream); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(conversation.counterpart)) { Conference new_conference = new Conference() { jid=conversation.counterpart, nick=new_nick, name=conference.name, password=conference.password, autojoin=conference.autojoin }; bookmarks_provider[conversation.account].replace_conference.begin(stream, conversation.counterpart, new_conference); break; } } } public void invite(Account account, Jid muc, Jid invitee) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).invite(stream, muc.bare_jid, invitee.bare_jid); } public void kick(Account account, Jid jid, string nick) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).kick(stream, jid.bare_jid, nick); } public void change_affiliation(Account account, Jid jid, string nick, string role) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, jid.bare_jid, null, nick, role); } public void change_role(Account account, Jid jid, string nick, string role) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_role(stream, jid.bare_jid, nick, role); } public void request_voice(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).request_voice(stream, jid.bare_jid); } public bool kick_possible(Account account, Jid occupant) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) return stream.get_module(Xep.Muc.Module.IDENTITY).kick_possible(stream, occupant); return false; } //the term `private room` is a short hand for members-only+non-anonymous rooms public bool is_private_room(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) { return false; } Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY); if (flag == null) { return false; } return flag.has_room_feature(jid, Xep.Muc.Feature.NON_ANONYMOUS) && flag.has_room_feature(jid, Xep.Muc.Feature.MEMBERS_ONLY); } public bool is_moderated_room(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) { return false; } Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY); if (flag == null) { return false; } return flag.has_room_feature(jid, Xep.Muc.Feature.MODERATED); } public bool is_public_room(Account account, Jid jid) { return is_groupchat(jid, account) && !is_private_room(account, jid); } public Gee.List? get_occupants(Jid jid, Account account) { if (is_groupchat(jid, account)) { Gee.List ret = new ArrayList(Jid.equals_func); Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(jid, account); if (full_jids != null) { ret.add_all(full_jids); // Remove eventual presence from bare jid ret.remove(jid); } return ret; } return null; } public Gee.List? get_other_occupants(Jid jid, Account account) { Gee.List? occupants = get_occupants(jid, account); Jid? own_jid = get_own_jid(jid, account); if (occupants != null && own_jid != null) { occupants.remove(own_jid); } return occupants; } public bool is_groupchat(Jid jid, Account account) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT); return !jid.is_full() && conversation != null; } public bool might_be_groupchat(Jid jid, Account account) { if (mucs_joining.has_key(account) && mucs_joining[account].contains(jid)) return true; return is_groupchat(jid, account); } public bool is_groupchat_occupant(Jid jid, Account account) { return is_groupchat(jid.bare_jid, account) && jid.resourcepart != null; } public async Set? get_bookmarks(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; return yield bookmarks_provider[account].get_conferences(stream); } public void add_bookmark(Account account, Conference conference) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { bookmarks_provider[account].add_conference.begin(stream, conference); } } public void remove_bookmark(Account account, Conference conference) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { bookmarks_provider[account].remove_conference.begin(stream, conference); } } public string? get_room_name(Account account, Jid jid) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_room_name(jid); } return null; } public string? get_groupchat_subject(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_muc_subject(jid.bare_jid); } return null; } public Jid? get_real_jid(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_real_jid(jid); } return null; } public Jid? get_occupant_jid(Account account, Jid room, Jid occupant_real_jid) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_occupant_jid(occupant_real_jid, room); } return null; } public Xep.Muc.Role? get_role(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_occupant_role(jid); } return null; } public Xep.Muc.Affiliation? get_affiliation(Jid muc_jid, Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_affiliation(muc_jid, jid); } return null; } public Gee.List? get_offline_members(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_offline_members(jid); } return null; } public Gee.List? get_other_offline_members(Jid jid, Account account) { Gee.List? occupants = get_offline_members(jid, account); if (occupants != null) { occupants.remove(account.bare_jid); } return occupants; } public Jid? get_own_jid(Jid muc_jid, Account account) { try { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { string? nick = flag.get_muc_nick(muc_jid); if (nick != null) return muc_jid.with_resource(nick); } } catch (InvalidJidError e) { warning("Joined MUC with invalid Jid: %s", e.message); } return null; } public bool is_own_muc_jid(Jid full_jid, Account account) { if (!is_groupchat(full_jid.bare_jid, account)) return false; Jid? own_jid = get_own_jid(full_jid, account); return own_jid != null && own_jid.equals(full_jid); } private Xep.Muc.Flag? get_muc_flag(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { return stream.get_flag(Xep.Muc.Flag.IDENTITY); } return null; } public bool is_joined(Jid jid, Account account) { return get_own_jid(jid, account) != null; } public string? get_own_occupant_id(Account account, Jid muc_jid) { if (own_occupant_ids.has_key(account) && own_occupant_ids[account].has_key(muc_jid)) { return own_occupant_ids[account][muc_jid]; } return null; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).self_removed_from_room.connect( (stream, jid, code) => { cancel_sync(account, jid); left(account, jid); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).subject_set.connect( (stream, subject, jid) => { subject_set(account, jid, subject); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).invite_received.connect( (stream, room_jid, from_jid, password, reason) => { on_invite_received(account, room_jid, from_jid, password, reason); }); stream_interactor.module_manager.get_module(account, Xep.DirectMucInvitations.Module.IDENTITY).invite_received.connect( (stream, room_jid, from_jid, password, reason) => { on_invite_received(account, room_jid, from_jid, password, reason); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).voice_request_received.connect( (stream, room_jid, from_jid, nick) => { voice_request_received(account, room_jid, from_jid, nick); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).received_occupant_role.connect( (stream, from_jid, role) => { received_occupant_role(account, from_jid, role); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_info_updated.connect( (stream, muc_jid) => { room_info_updated(account, muc_jid); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).received_occupant_jid.connect( (stream, room, occupant) => { if (is_private_room(account, room.bare_jid)) { private_room_occupant_updated(account, room, occupant); } }); stream_interactor.module_manager.get_module(account, Xep.OccupantIds.Module.IDENTITY).received_own_occupant_id.connect( (stream, jid, occupant_id) => { if (!own_occupant_ids.has_key(account)) { own_occupant_ids[account] = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); } own_occupant_ids[account][jid] = occupant_id; }); } private async void search_default_muc_server(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; ServiceDiscovery.ItemsResult? items_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name); if (items_result == null) return; for (int i = 0; i < 2; i++) { foreach (Xep.ServiceDiscovery.Item item in items_result.items) { // First try the promising items and only afterwards all the others bool promising_upload_item = item.jid.to_string().has_prefix("conference") || item.jid.to_string().has_prefix("muc") || item.jid.to_string().has_prefix("chat"); if ((i == 0 && !promising_upload_item) || (i == 1) && promising_upload_item) continue; Gee.Set identities = yield stream_interactor.get_module(EntityInfo.IDENTITY).get_identities(account, item.jid); if (identities == null) return; foreach (Xep.ServiceDiscovery.Identity identity in identities) { if (identity.category == Xep.ServiceDiscovery.Identity.CATEGORY_CONFERENCE) { default_muc_server[account] = item.jid; debug("[%s] Default MUC: %s", account.bare_jid.to_string(), item.jid.to_string()); return; } } } } } private async void on_stream_negotiated(Account account, XmppStream stream) { if (mucs_sync_cancellables.has_key(account)) { foreach (Cancellable cancellable in mucs_sync_cancellables[account].values) { if (!cancellable.is_cancelled()) { cancellable.cancel(); } } } yield initialize_bookmarks_provider(account); Set? conferences = yield bookmarks_provider[account].get_conferences(stream); if (conferences == null) { join_all_active(account); } else { sync_autojoin_active(account, conferences); } if (!default_muc_server.has_key(account)) { search_default_muc_server.begin(account); } } private async void initialize_bookmarks_provider(Account account) { if (bookmarks_provider.has_key(account)) return; // Use PEP native bookmarks (urn:xmpp:bookmarks:1) if conversion is available, legacy bookmarks (storage:bookmarks) otherwise. bool has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(account, account.bare_jid, Xep.Bookmarks2.NS_URI_COMPAT); if (has_feature) { debug("[%s] Using PEP native bookmarks (urn:xmpp:bookmarks:1)", account.bare_jid.to_string()); bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks2.Module.IDENTITY); } else { debug("[%s] Using legacy bookmarks (storage:bookmarks)", account.bare_jid.to_string()); bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks.Module.IDENTITY); } bookmarks_provider[account].received_conferences.connect( (stream, conferences) => { sync_autojoin_active(account, conferences); bookmarks_updated(account, conferences); }); bookmarks_provider[account].conference_added.connect( (stream, conference) => { on_conference_added(account, conference); }); bookmarks_provider[account].conference_removed.connect( (stream, jid) => { on_conference_removed(account, jid); }); } private void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) { if (!invites.has_key(account)) { invites[account] = new LinkedList(Jid.equals_func); } if (invites[account].contains(room_jid)) return; invites[account].add(room_jid); invite_received(account, room_jid, from_jid, password, reason); Timeout.add_seconds(5, () => { // We don't want to show the same invite (direct+mediated) twice, but a distinct invite is fine invites[account].remove(room_jid); return false; }); } private void join_all_active(Account account) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); foreach (Conversation conversation in conversations) { if (conversation.type_ == Conversation.Type.GROUPCHAT && conversation.nickname != null) { join.begin(account, conversation.counterpart, conversation.nickname, null); } } } private void sync_autojoin_active(Account account, Set conferences) { Gee.List active_conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); // Join auto-join MUCs foreach (Conference conference in conferences) { if (!conference.autojoin) continue; bool is_active = false; foreach (Conversation conversation in active_conversations) { if (conference.jid.equals(conversation.counterpart)) { is_active = true; } } if (!is_active || !is_joined(conference.jid, account)) { join.begin(account, conference.jid, conference.nick, conference.password, true); } } // Part MUCs that aren't auto-join (which closes those conversations) foreach (Conversation conversation in active_conversations) { if (conversation.type_ != Conversation.Type.GROUPCHAT) continue; bool should_be_active = false; foreach (Conference conference in conferences) { if (conference.jid.equals(conversation.counterpart) && conference.autojoin) { should_be_active = true; } } if (!should_be_active) { part(conversation.account, conversation.counterpart); } } } private void set_autojoin(Account account, XmppStream stream, Jid jid, string? nick, string? password) { bookmarks_provider[account].get_conferences.begin(stream, (_, res) => { Set? conferences = bookmarks_provider[account].get_conferences.end(res); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(jid)) { if (!conference.autojoin) { Conference new_conference = new Conference() { jid=jid, nick=nick ?? conference.nick, name=conference.name, password=password ?? conference.password, autojoin=true }; bookmarks_provider[account].replace_conference.begin(stream, jid, new_conference); } return; } } Conference changed = new Xep.Bookmarks.Bookmarks1Conference(jid) { nick=nick, password=password, autojoin=true }; bookmarks_provider[account].add_conference.begin(stream, changed); }); } private void unset_autojoin(Account account, XmppStream stream, Jid jid) { bookmarks_provider[account].get_conferences.begin(stream, (_, res) => { Set? conferences = bookmarks_provider[account].get_conferences.end(res); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(jid)) { if (conference.autojoin) { Conference new_conference = new Conference() { jid=jid, nick=conference.nick, name=conference.name, password=conference.password, autojoin=false }; bookmarks_provider[account].replace_conference.begin(stream, jid, new_conference); return; } } } }); } private void on_conference_added(Account account, Xmpp.Conference conference) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conference.jid, account, Conversation.Type.GROUPCHAT); if (conversation != null) { if (!conversation.active && conference.autojoin) { join.begin(account, conference.jid, conference.nick, conference.password); } else if (conversation.active && !conference.autojoin) { part(account, conference.jid); } } if (conference.autojoin) { join.begin(account, conference.jid, conference.nick, conference.password); } conference_added(account, conference); } private void on_conference_removed(Account account, Jid jid) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT); if (conversation != null && conversation.active) { part(account, jid); } conference_removed(account, jid); } private void self_ping(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; if (!mucs_todo.has_key(account)) return; foreach (Jid jid in mucs_todo[account]) { bool joined = false; Xmpp.Xep.MucSelfPing.is_joined.begin(stream, jid, (_, res) => { joined = Xmpp.Xep.MucSelfPing.is_joined.end(res); }); Timeout.add_seconds(10, () => { if (joined || !mucs_todo.has_key(account) || stream_interactor.get_stream(account) != stream) return false; join.begin(account, jid.bare_jid, jid.resourcepart, null, true); return false; }); } } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "MUC"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public ReceivedMessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (conversation.type_ != Conversation.Type.GROUPCHAT) return false; XmppStream stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; if (Xep.DelayedDelivery.MessageFlag.get_flag(stanza) == null) { Jid? real_jid = stream.get_flag(Xep.Muc.Flag.IDENTITY).get_real_jid(message.counterpart); if (real_jid != null && !real_jid.equals(message.counterpart)) { message.real_jid = real_jid.bare_jid; } } Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(message.counterpart.bare_jid, conversation.account); if (stanza.id != null && own_muc_jid != null && message.from.equals(own_muc_jid)) { Entities.Message? m = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza.id, conversation); if (m != null) { // For own messages from this device (msg is a duplicate) m.marked = Message.Marked.RECEIVED; string? server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(stanza, m.counterpart.bare_jid); if (server_id != null) { m.server_id = server_id; } } // For own messages from other devices (msg is not a duplicate msg) message.marked = Message.Marked.RECEIVED; } return false; } } } } dino-0.4.3/libdino/src/service/notification_events.vala0000644000000000000000000002315214452563620021745 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public class NotificationEvents : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("notification_events"); public string id { get { return IDENTITY.id; } } public signal void notify_content_item(ContentItem content_item, Conversation conversation); private StreamInteractor stream_interactor; private Future notifier; private Promise notifier_promise; private bool notifier_outstanding = true; public static void start(StreamInteractor stream_interactor) { NotificationEvents m = new NotificationEvents(stream_interactor); stream_interactor.add_module(m); } public NotificationEvents(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect((item, conversation) => on_content_item_received.begin(item, conversation)); stream_interactor.get_module(PresenceManager.IDENTITY).received_subscription_request.connect((jid, account) => on_received_subscription_request.begin(jid, account)); stream_interactor.get_module(MucManager.IDENTITY).invite_received.connect((account, room_jid, from_jid, password, reason) => on_invite_received.begin(account, room_jid, from_jid, password, reason)); stream_interactor.get_module(MucManager.IDENTITY).voice_request_received.connect((account, room_jid, from_jid, nick) => on_voice_request_received.begin(account, room_jid, from_jid, nick)); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state, conversation, video, multiparty) => on_call_incoming.begin(call, state, conversation, video, multiparty)); stream_interactor.connection_manager.connection_error.connect((account, error) => on_connection_error.begin(account, error)); stream_interactor.get_module(ChatInteraction.IDENTITY).focused_in.connect((conversation) => on_focused_in.begin(conversation)); notifier_promise = new Promise(); notifier = notifier_promise.future; } public async void register_notification_provider(NotificationProvider notification_provider) { if (notifier_outstanding || (yield notifier.wait_async()).get_priority() < notification_provider.get_priority()) { notifier_outstanding = false; notifier_promise.set_value(notification_provider); } } private async void on_content_item_received(ContentItem item, Conversation conversation) { ContentItem last_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); if (item.id != last_item.id) return; if (item.id == conversation.read_up_to_item) return; if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus()) return; Conversation.NotifySetting notify = conversation.get_notification_setting(stream_interactor); if (notify == Conversation.NotifySetting.OFF) return; string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); string? participant_display_name = null; if (conversation.type_ == Conversation.Type.GROUPCHAT) { participant_display_name = get_participant_display_name(stream_interactor, conversation, item.jid); } switch (item.type_) { case MessageItem.TYPE: Message message = ((MessageItem) item).message; if (message.direction == Message.DIRECTION_SENT) return; if (notify == Conversation.NotifySetting.HIGHLIGHT) { Jid? nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (nick == null) return; bool highlight = Regex.match_simple("\\b" + Regex.escape_string(nick.resourcepart) + "\\b", message.body, RegexCompileFlags.CASELESS); if (!highlight) return; } notify_content_item(item, conversation); if (notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_message(message, conversation, conversation_display_name, participant_display_name); } break; case FileItem.TYPE: FileTransfer file_transfer = ((FileItem) item).file_transfer; bool is_image = file_transfer.mime_type != null && file_transfer.mime_type.has_prefix("image"); // Don't notify on file transfers in a groupchat set to "mention only" if (notify == Conversation.NotifySetting.HIGHLIGHT) return; if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return; notify_content_item(item, conversation); if (notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_file(file_transfer, conversation, is_image, conversation_display_name, participant_display_name); } break; case CallItem.TYPE: // handled in `on_call_incoming` break; } } private async void on_voice_request_received(Account account, Jid room_jid, Jid from_jid, string nick) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(room_jid, account, Conversation.Type.GROUPCHAT); if (conversation == null) return; NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_voice_request(conversation, from_jid); } private async void on_received_subscription_request(Jid jid, Account account) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus(conversation)) return; NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_subscription_request(conversation); } private async void on_call_incoming(Call call, CallState call_state, Conversation conversation, bool video, bool multiparty) { if (!stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) return; string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_call(call, conversation, video, multiparty, conversation_display_name); call.notify["state"].connect(() => { if (call.state != Call.State.RINGING) { notifier.retract_call_notification.begin(call, conversation); } }); } private async void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) { string inviter_display_name; if (room_jid.equals_bare(from_jid)) { Conversation conversation = new Conversation(room_jid, account, Conversation.Type.GROUPCHAT); inviter_display_name = get_participant_display_name(stream_interactor, conversation, from_jid); } else { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); inviter_display_name = get_participant_display_name(stream_interactor, direct_conversation, from_jid); } NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_muc_invite(account, room_jid, from_jid, inviter_display_name); } private async void on_connection_error(Account account, ConnectionManager.ConnectionError error) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_connection_error(account, error); } private async void on_focused_in(Conversation conversation) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.retract_content_item_notifications(); yield notifier.retract_conversation_notifications(conversation); } } public interface NotificationProvider : Object { public abstract double get_priority(); public abstract async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name); public abstract async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name); public abstract async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name); public abstract async void retract_call_notification(Call call, Conversation conversation); public abstract async void notify_subscription_request(Conversation conversation); public abstract async void notify_connection_error(Account account, ConnectionManager.ConnectionError error); public abstract async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name); public abstract async void notify_voice_request(Conversation conversation, Jid from_jid); public abstract async void retract_content_item_notifications(); public abstract async void retract_conversation_notifications(Conversation conversation); } } dino-0.4.3/libdino/src/service/presence_manager.vala0000644000000000000000000001174314452563620021174 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class PresenceManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("presence_manager"); public string id { get { return IDENTITY.id; } } public signal void show_received(Jid jid, Account account); public signal void received_offline_presence(Jid jid, Account account); public signal void received_subscription_request(Jid jid, Account account); public signal void received_subscription_approval(Jid jid, Account account); private StreamInteractor stream_interactor; private HashMap> resources = new HashMap>(Jid.hash_bare_func, Jid.equals_bare_func); private Gee.List subscription_requests = new ArrayList(Jid.equals_func); public static void start(StreamInteractor stream_interactor) { PresenceManager m = new PresenceManager(stream_interactor); stream_interactor.add_module(m); } private PresenceManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); } public string? get_last_show(Jid jid, Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; Xmpp.Presence.Stanza? presence = stream.get_flag(Presence.Flag.IDENTITY).get_presence(jid); if (presence == null) return null; return presence.show; } public Gee.List? get_full_jids(Jid jid, Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { Xmpp.Presence.Flag flag = stream.get_flag(Presence.Flag.IDENTITY); if (flag == null) return null; return flag.get_resources(jid.bare_jid); } return null; } public bool exists_subscription_request(Account account, Jid jid) { return subscription_requests.contains(jid); } public void request_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Presence.Module.IDENTITY).request_subscription(stream, jid.bare_jid); } public void approve_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { stream.get_module(Xmpp.Presence.Module.IDENTITY).approve_subscription(stream, jid.bare_jid); subscription_requests.remove(jid); } } public void deny_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { stream.get_module(Xmpp.Presence.Module.IDENTITY).deny_subscription(stream, jid.bare_jid); subscription_requests.remove(jid); } } public void cancel_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Presence.Module.IDENTITY).cancel_subscription(stream, jid.bare_jid); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available_show.connect((stream, jid, show) => on_received_available_show(account, jid, show) ); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_unavailable.connect((stream, presence) => on_received_unavailable(account, presence.from) ); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_subscription_request.connect((stream, jid) => { if (!subscription_requests.contains(jid)) { subscription_requests.add(jid); } received_subscription_request(jid, account); }); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_subscription_approval.connect((stream, jid) => { received_subscription_approval(jid, account); }); } private void on_received_available_show(Account account, Jid jid, string show) { lock (resources) { if (!resources.has_key(jid)){ resources[jid] = new ArrayList(Jid.equals_func); } if (!resources[jid].contains(jid)) { resources[jid].add(jid); } } show_received(jid, account); } private void on_received_unavailable(Account account, Jid jid) { lock (resources) { if (resources.has_key(jid)) { resources[jid].remove(jid); if (resources[jid].size == 0 || jid.is_bare()) { resources.unset(jid); } } } received_offline_presence(jid, account); } } } dino-0.4.3/libdino/src/service/reactions.vala0000644000000000000000000005270314452563620017666 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.Reactions : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("reactions"); public string id { get { return IDENTITY.id; } } public signal void reaction_added(Account account, int content_item_id, Jid jid, string reaction); public signal void reaction_removed(Account account, int content_item_id, Jid jid, string reaction); private StreamInteractor stream_interactor; private Database db; private HashMap> reaction_infos = new HashMap>(); public static void start(StreamInteractor stream_interactor, Database database) { Reactions m = new Reactions(stream_interactor, database); stream_interactor.add_module(m); } private Reactions(StreamInteractor stream_interactor, Database database) { this.stream_interactor = stream_interactor; this.db = database; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_new_item); } public void add_reaction(Conversation conversation, ContentItem content_item, string reaction) { Gee.List reactions = get_own_reactions(conversation, content_item); if (!reactions.contains(reaction)) { reactions.add(reaction); } try { send_reactions(conversation, content_item, reactions); reaction_added(conversation.account, content_item.id, conversation.account.bare_jid, reaction); } catch (IOError e) {} } public void remove_reaction(Conversation conversation, ContentItem content_item, string reaction) { Gee.List reactions = get_own_reactions(conversation, content_item); reactions.remove(reaction); try { send_reactions(conversation, content_item, reactions); reaction_removed(conversation.account, content_item.id, conversation.account.bare_jid, reaction); } catch (IOError e) {} } public Gee.List get_item_reactions(Conversation conversation, ContentItem content_item) { if (conversation.type_ == Conversation.Type.CHAT) { return get_chat_message_reactions(conversation.account, content_item); } else { return get_muc_message_reactions(conversation.account, content_item); } } public bool conversation_supports_reactions(Conversation conversation) { if (conversation.type_ == Conversation.Type.CHAT) { return true; } else { // The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids) var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (!server_supports_sid) return false; bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI); if (supports_occupant_ids) return true; return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); } } private void send_reactions(Conversation conversation, ContentItem content_item, Gee.List reactions) throws IOError { string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (message_id == null) throw new IOError.FAILED("No message for content_item"); XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) throw new IOError.NOT_CONNECTED("No stream"); var reactions_module = stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY); if (conversation.type_ == Conversation.Type.GROUPCHAT) { reactions_module.send_reaction.begin(stream, conversation.counterpart, "groupchat", message_id, reactions); // We save the reaction when it gets reflected back to us } else if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { reactions_module.send_reaction.begin(stream, conversation.counterpart, "chat", message_id, reactions); } else if (conversation.type_ == Conversation.Type.CHAT) { int64 now_millis = GLib.get_real_time () / 1000; reactions_module.send_reaction.begin(stream, conversation.counterpart, "chat", message_id, reactions, (_, res) => { try { reactions_module.send_reaction.end(res); save_chat_reactions(conversation.account, conversation.account.bare_jid, content_item.id, now_millis, reactions); } catch (IOError e) {} }); } } private Gee.List get_own_reactions(Conversation conversation, ContentItem content_item) { if (conversation.type_ == Conversation.Type.CHAT) { return get_chat_user_reactions(conversation.account, content_item.id, conversation.account.bare_jid) .emojis; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { string own_occupant_id = stream_interactor.get_module(MucManager.IDENTITY).get_own_occupant_id(conversation.account, content_item.jid); return get_muc_user_reactions(conversation.account, content_item.id, own_occupant_id, conversation.account.bare_jid) .emojis; } return new ArrayList(); } private class ReactionsTime { public Gee.List? emojis = null; public int64 time = -1; } private ReactionsTime get_chat_user_reactions(Account account, int content_item_id, Jid jid) { int jid_id = db.get_jid_id(jid); QueryBuilder query = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item_id) .with(db.reaction.jid_id, "=", jid_id); RowOption row = query.single().row(); ReactionsTime ret = new ReactionsTime(); if (row.is_present()) { ret.emojis = string_to_emoji_list(row[db.reaction.emojis]); ret.time = row[db.reaction.time]; } else { ret.emojis = new ArrayList(); ret.time = -1; } return ret; } private ReactionsTime get_muc_user_reactions(Account account, int content_item_id, string? occupant_id, Jid? real_jid) { if (occupant_id == null && real_jid == null) critical("Need occupant id or real jid of a reaction"); QueryBuilder query = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item_id) .outer_join_with(db.occupantid, db.occupantid.id, db.reaction.occupant_id); if (occupant_id != null) { query.with(db.occupantid.occupant_id, "=", occupant_id); } else if (real_jid != null) { query.with(db.reaction.jid_id, "=", db.get_jid_id(real_jid)); } RowOption row = query.single().row(); ReactionsTime ret = new ReactionsTime(); if (row.is_present()) { ret.emojis = string_to_emoji_list(row[db.reaction.emojis]); ret.time = row[db.reaction.time]; } else { ret.emojis = new ArrayList(); ret.time = -1; } return ret; } private Gee.List string_to_emoji_list(string emoji_str) { ArrayList ret = new ArrayList(); foreach (string emoji in emoji_str.split(",")) { if (emoji.length != 0) ret.add(emoji); } return ret; } public Gee.List get_chat_message_reactions(Account account, ContentItem content_item) { QueryBuilder select = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item.id) .order_by(db.reaction.time, "DESC"); var ret = new ArrayList(); var index = new HashMap(); foreach (Row row in select) { string emoji_str = row[db.reaction.emojis]; Jid jid = db.get_jid_by_id(row[db.reaction.jid_id]); foreach (string emoji in emoji_str.split(",")) { if (!index.has_key(emoji)) { index[emoji] = new ReactionUsers() { reaction=emoji, jids=new ArrayList(Jid.equals_func) }; ret.add(index[emoji]); } index[emoji].jids.add(jid); } } return ret; } public Gee.List get_muc_message_reactions(Account account, ContentItem content_item) { QueryBuilder select = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item.id) .outer_join_with(db.occupantid, db.occupantid.id, db.reaction.occupant_id) .outer_join_with(db.jid, db.jid.id, db.reaction.jid_id) .order_by(db.reaction.time, "DESC"); string? own_occupant_id = stream_interactor.get_module(MucManager.IDENTITY).get_own_occupant_id(account, content_item.jid); var ret = new ArrayList(); var index = new HashMap(); foreach (Row row in select) { string emoji_str = row[db.reaction.emojis]; Jid jid = null; if (!db.jid.bare_jid.is_null(row)) { jid = new Jid(row[db.jid.bare_jid]); } else if (!db.occupantid.occupant_id.is_null(row)) { if (row[db.occupantid.occupant_id] == own_occupant_id) { jid = account.bare_jid; } else { string nick = row[db.occupantid.last_nick]; jid = content_item.jid.with_resource(nick); } } else { warning("Reaction with neither JID nor occupant id"); } foreach (string emoji in emoji_str.split(",")) { if (!index.has_key(emoji)) { index[emoji] = new ReactionUsers() { reaction=emoji, jids=new ArrayList(Jid.equals_func) }; ret.add(index[emoji]); } index[emoji].jids.add(jid); } } return ret; } private void on_account_added(Account account) { // TODO get time from delays stream_interactor.module_manager.get_module(account, Xmpp.Xep.Reactions.Module.IDENTITY).received_reactions.connect((stream, from_jid, message_id, reactions, stanza) => { on_reaction_received.begin(account, from_jid, message_id, reactions, stanza); }); } private async void on_reaction_received(Account account, Jid from_jid, string message_id, Gee.List reactions, MessageStanza stanza) { if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { // Apply the same restrictions for incoming reactions as we do on sending them Conversation muc_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(from_jid, account.bare_jid, account, MessageStanza.TYPE_GROUPCHAT); bool muc_supports_reactions = conversation_supports_reactions(muc_conversation); if (!muc_supports_reactions) return; } Message reaction_message = yield stream_interactor.get_module(MessageProcessor.IDENTITY).parse_message_stanza(account, stanza); Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(reaction_message); int content_item_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_id_for_message_id(conversation, message_id); var reaction_info = new ReactionInfo() { conversation=conversation, from_jid=from_jid, reactions=reactions, stanza=stanza, received_time=new DateTime.now() }; if (content_item_id != -1) { process_reaction_for_message(content_item_id, reaction_info); return; } // Store reaction infos for later processing after we got the message debug("Got reaction for %s but dont have message yet %s", message_id, db.get_jid_id(stanza.from.bare_jid).to_string()); if (!reaction_infos.has_key(message_id)) { reaction_infos[message_id] = new ArrayList(); } reaction_infos[message_id].add(reaction_info); } /* * When we get a new ContentItem, check if we have any reactions cached that apply to it. * If so, process the reactions, map and store them. */ private void on_new_item(ContentItem item, Conversation conversation) { string? stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, item); if (stanza_id == null) return; Gee.List? reaction_info_list = reaction_infos[stanza_id]; if (reaction_info_list == null) return; Message? message = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_for_content_item(conversation, item); if (message == null) return; // Check if the (or potentially which) reaction fits the message var applicable_reactions = new ArrayList(); applicable_reactions.add_all_iterator(reaction_info_list.filter(info => info.conversation.equals(conversation))); foreach (ReactionInfo applicable_reaction in applicable_reactions) { reaction_info_list.remove(applicable_reaction); if (reaction_info_list.is_empty) { reaction_infos.unset(stanza_id); } debug("Got ContentItem for reaction %s", stanza_id); process_reaction_for_message(item.id, applicable_reaction); } } private Message? get_message_for_reaction(Conversation conversation, string message_id) { // Query message from a specific account and counterpart. This also makes sure it's a valid reaction for the message. if (conversation.type_ == Conversation.Type.CHAT) { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_id, conversation); } else { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(message_id, conversation); } } private void process_reaction_for_message(int content_item_id, ReactionInfo reaction_info) { Account account = reaction_info.conversation.account; MessageStanza stanza = reaction_info.stanza; Jid from_jid = reaction_info.from_jid; Gee.List reactions = reaction_info.reactions; // Get reaction time DateTime? reaction_time = null; DelayedDelivery.MessageFlag? delayed_message_flag = DelayedDelivery.MessageFlag.get_flag(stanza); if (delayed_message_flag != null) { reaction_time = delayed_message_flag.datetime; } if (reaction_time == null) { MessageArchiveManagement.MessageFlag? mam_message_flag = MessageArchiveManagement.MessageFlag.get_flag(stanza); if (mam_message_flag != null) reaction_time = mam_message_flag.server_time; } var time_now = new DateTime.now_local(); if (reaction_time == null) reaction_time = time_now; if (reaction_time.compare(time_now) > 0) { reaction_time = reaction_info.received_time; } int64 reaction_time_long = (int64) (reaction_time.to_unix() * 1000 + reaction_time.get_microsecond() / 1000); // Get current reactions string? occupant_id = OccupantIds.get_occupant_id(stanza.stanza); Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(from_jid, account); if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT && occupant_id == null && real_jid == null) { warning("Attempting to add reaction to message w/o knowing occupant id or real jid"); return; } ReactionsTime reactions_time = null; if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { reactions_time = get_muc_user_reactions(account, content_item_id, occupant_id, real_jid); } else { reactions_time = get_chat_user_reactions(account, content_item_id, from_jid); } if (reaction_time_long <= reactions_time.time) { // We already have a more recent reaction return; } // Save reactions if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { save_muc_reactions(account, content_item_id, from_jid, occupant_id, real_jid, reaction_time_long, reactions); } else { save_chat_reactions(account, from_jid, content_item_id, reaction_time_long, reactions); } // Notify about reaction changes Gee.List? current_reactions = reactions_time.emojis; Jid signal_jid = from_jid; if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT && signal_jid.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(from_jid, account))) { signal_jid = account.bare_jid; } else if (stanza.type_ == MessageStanza.TYPE_CHAT) { signal_jid = signal_jid.bare_jid; } foreach (string current_reaction in current_reactions) { if (!reactions.contains(current_reaction)) { reaction_removed(account, content_item_id, signal_jid, current_reaction); } } foreach (string new_reaction in reactions) { if (!current_reactions.contains(new_reaction)) { reaction_added(account, content_item_id, signal_jid, new_reaction); } } debug("reactions were: "); foreach (string reac in current_reactions) { debug(reac); } debug("reactions new : "); foreach (string reac in reactions) { debug(reac); } } private void save_chat_reactions(Account account, Jid jid, int content_item_id, int64 reaction_time, Gee.List reactions) { var emoji_builder = new StringBuilder(); for (int i = 0; i < reactions.size; i++) { if (i != 0) emoji_builder.append(","); emoji_builder.append(reactions[i]); } db.reaction.upsert() .value(db.reaction.account_id, account.id, true) .value(db.reaction.content_item_id, content_item_id, true) .value(db.reaction.jid_id, db.get_jid_id(jid), true) .value(db.reaction.emojis, emoji_builder.str, false) .value(db.reaction.time, (long)reaction_time, false) .perform(); } private void save_muc_reactions(Account account, int content_item_id, Jid jid, string? occupant_id, Jid? real_jid, int64 reaction_time, Gee.List reactions) { assert(occupant_id != null || real_jid != null); int jid_id = db.get_jid_id(jid); var emoji_builder = new StringBuilder(); for (int i = 0; i < reactions.size; i++) { if (i != 0) emoji_builder.append(","); emoji_builder.append(reactions[i]); } var builder = db.reaction.upsert() .value(db.reaction.account_id, account.id, true) .value(db.reaction.content_item_id, content_item_id, true) .value(db.reaction.emojis, emoji_builder.str, false) .value(db.reaction.time, (long)reaction_time, false); if (real_jid != null) { builder.value(db.reaction.jid_id, db.get_jid_id(real_jid), occupant_id == null); } if (occupant_id != null) { RowOption row = db.occupantid.select() .with(db.occupantid.account_id, "=", account.id) .with(db.occupantid.jid_id, "=", jid_id) .with(db.occupantid.occupant_id, "=", occupant_id) .single().row(); int occupant_db_id = -1; if (row.is_present()) { occupant_db_id = row[db.occupantid.id]; } else { occupant_db_id = (int)db.occupantid.upsert() .value(db.occupantid.account_id, account.id, true) .value(db.occupantid.jid_id, jid_id, true) .value(db.occupantid.occupant_id, occupant_id, true) .value(db.occupantid.last_nick, jid.resourcepart, false) .perform(); } builder.value(db.reaction.occupant_id, occupant_db_id, true); } builder.perform(); } } public class Dino.ReactionUsers { public string reaction { get; set; } public Gee.List jids { get; set; } } public class Dino.ReactionInfo { public Conversation conversation { get; set; } public Jid from_jid { get; set; } public Gee.List reactions { get; set; } public MessageStanza stanza { get; set; } public DateTime received_time { get; set; } } dino-0.4.3/libdino/src/service/registration.vala0000644000000000000000000001725114452563620020410 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class Register : StreamInteractionModule, Object{ public static ModuleIdentity IDENTITY = new ModuleIdentity("registration"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { Register m = new Register(stream_interactor, db); stream_interactor.add_module(m); } private Register(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public async ConnectionManager.ConnectionError.Source? add_check_account(Account account) { ConnectionManager.ConnectionError.Source? ret = null; Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Sasl.Module(account.bare_jid.to_string(), account.password)); XmppStreamResult stream_result = yield Xmpp.establish_stream(account.bare_jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(account.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.tls_errors != null) { ret = ConnectionManager.ConnectionError.Source.TLS; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = add_check_account.callback; stream.stream_negotiated.connect(() => { if (callback == null) return; Idle.add((owned)callback); }); stream.get_module(Sasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => { if (callback == null) return; ret = ConnectionManager.ConnectionError.Source.SASL; Idle.add((owned)callback); }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { ret = ConnectionManager.ConnectionError.Source.CONNECTION; Idle.add((owned)callback); } }); yield; try { yield stream_result.stream.disconnect(); } catch (Error e) {} return ret; } public class ServerAvailabilityReturn { public bool available { get; set; } public TlsCertificateFlags? error_flags { get; set; } } public static async ServerAvailabilityReturn check_server_availability(Jid jid) { ServerAvailabilityReturn ret = new ServerAvailabilityReturn() { available=false }; Gee.List list = new ArrayList(); list.add(new Iq.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.io_error != null) { debug("Error connecting to stream: %s", stream_result.io_error.message); } if (stream_result.tls_errors != null) { ret.error_flags = stream_result.tls_errors; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = check_server_availability.callback; stream.stream_negotiated.connect(() => { if (callback != null) { ret.available = true; Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; try { yield stream.disconnect(); } catch (Error e) {} return ret; } public class RegistrationFormReturn { public Xep.InBandRegistration.Form? form { get; set; } public TlsCertificateFlags? error_flags { get; set; } } public static async RegistrationFormReturn get_registration_form(Jid jid) { RegistrationFormReturn ret = new RegistrationFormReturn(); Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.io_error != null) { debug("Error connecting to stream: %s", stream_result.io_error.message); } if (stream_result.tls_errors != null) { ret.error_flags = stream_result.tls_errors; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = get_registration_form.callback; stream.stream_negotiated.connect(() => { if (callback != null) { Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; if (stream.negotiation_complete) { ret.form = yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).get_from_server(stream, jid); } try { yield stream.disconnect(); } catch (Error e) {} return ret; } public static async string? submit_form(Jid jid, Xep.InBandRegistration.Form form) { Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { return null; } XmppStream stream = stream_result.stream; SourceFunc callback = submit_form.callback; stream.stream_negotiated.connect(() => { if (callback != null) { Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; string? ret = null; if (stream.negotiation_complete) { ret = yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).submit_to_server(stream, jid, form); } try { yield stream.disconnect(); } catch (Error e) {} return ret; } } } dino-0.4.3/libdino/src/service/replies.vala0000644000000000000000000001173714452563620017344 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.Replies : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("reply"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private HashMap>> unmapped_replies = new HashMap>>(); private ReceivedMessageListener received_message_listener; public static void start(StreamInteractor stream_interactor, Database db) { Replies m = new Replies(stream_interactor, db); stream_interactor.add_module(m); } private Replies(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.received_message_listener = new ReceivedMessageListener(stream_interactor, this); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); } public ContentItem? get_quoted_content_item(Message message, Conversation conversation) { if (message.quoted_item_id == 0) return null; RowOption row_option = db.reply.select().with(db.reply.message_id, "=", message.id).row(); if (row_option.is_present()) { return stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, row_option[db.reply.quoted_content_item_id]); } return null; } public void set_message_is_reply_to(Message message, ContentItem reply_to) { message.quoted_item_id = reply_to.id; db.reply.upsert() .value(db.reply.message_id, message.id, true) .value(db.reply.quoted_content_item_id, reply_to.id) .value_null(db.reply.quoted_message_stanza_id) .value_null(db.reply.quoted_message_from) .perform(); } private void on_incoming_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { // Check if a previous message was in reply to this one var reply_qry = db.reply.select(); if (conversation.type_ == Conversation.Type.GROUPCHAT) { reply_qry.with(db.reply.quoted_message_stanza_id, "=", message.server_id); } else { reply_qry.with(db.reply.quoted_message_stanza_id, "=", message.stanza_id); } reply_qry.join_with(db.message, db.reply.message_id, db.message.id) .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.time, ">", (long)message.time.to_unix()) .order_by(db.message.time, "DESC"); foreach (Row reply_row in reply_qry) { ContentItem? message_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); Message? reply_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(reply_row[db.message.id], conversation); if (message_item != null && reply_message != null) { set_message_is_reply_to(reply_message, message_item); } } // Handle if this message is a reply Xep.Replies.ReplyTo? reply_to = Xep.Replies.get_reply_to(stanza); if (reply_to == null) return; ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_for_message_id(conversation, reply_to.to_message_id); if (quoted_content_item == null) return; set_message_is_reply_to(message, quoted_content_item); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE", "STORE_CONTENT_ITEM" }; public override string action_group { get { return "Quote"; } } public override string[] after_actions { get { return after_actions_const; } } private Replies outer; public ReceivedMessageListener(StreamInteractor stream_interactor, Replies outer) { this.outer = outer; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { outer.on_incoming_message(message, stanza, conversation); return false; } } } namespace Dino { public string message_body_without_reply_fallback(Message message) { string body = message.body; foreach (var fallback in message.get_fallbacks()) { if (fallback.ns_uri == Xep.Replies.NS_URI && message.quoted_item_id > 0) { body = body[0:body.index_of_nth_char(fallback.locations[0].from_char)] + body[body.index_of_nth_char(fallback.locations[0].to_char):body.length]; } } return body; } } dino-0.4.3/libdino/src/service/roster_manager.vala0000644000000000000000000001341214452563620020701 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class RosterManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("roster_manager"); public string id { get { return IDENTITY.id; } } public signal void removed_roster_item(Account account, Jid jid, Roster.Item roster_item); public signal void updated_roster_item(Account account, Jid jid, Roster.Item roster_item); public signal void mutual_subscription(Account account, Jid jid); private StreamInteractor stream_interactor; private Database db; private Gee.Map roster_stores = new HashMap(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { RosterManager m = new RosterManager(stream_interactor, db); stream_interactor.add_module(m); } public RosterManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.module_manager.initialize_account_modules.connect((account, modules) => { if (!roster_stores.has_key(account)) roster_stores[account] = new RosterStoreImpl(account, db); modules.add(new Roster.VersioningModule(roster_stores[account])); }); } public Collection get_roster(Account account) { if (roster_stores[account] == null) return new ArrayList(); return roster_stores[account].get_roster(); } public Roster.Item? get_roster_item(Account account, Jid jid) { if (roster_stores[account] == null) return null; return roster_stores[account].get_item(jid); } public void remove_jid(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).remove_jid(stream, jid.bare_jid); } public void add_jid(Account account, Jid jid, string? handle) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).add_jid(stream, jid.bare_jid, handle); } public void set_jid_handle(Account account, Jid jid, string? handle) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).set_jid_handle(stream, jid.bare_jid, handle); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).received_roster.connect_after( (stream, roster) => { foreach (Roster.Item roster_item in roster) { on_roster_item_updated(account, roster_item); } }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).item_removed.connect_after( (stream, roster_item) => { removed_roster_item(account, roster_item.jid, roster_item); }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).item_updated.connect_after( (stream, roster_item) => { on_roster_item_updated(account, roster_item); }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).mutual_subscription.connect_after( (stream, jid) => { mutual_subscription(account, jid); }); } private void on_roster_item_updated(Account account, Roster.Item roster_item) { updated_roster_item(account, roster_item.jid, roster_item); } } public class RosterStoreImpl : Roster.Storage, Object { private Account account; private Database db; private HashMap items = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); public class RosterStoreImpl(Account account, Database db) { this.account = account; this.db = db; foreach (Qlite.Row row in db.roster.select().with(db.roster.account_id, "=", account.id)) { try { Roster.Item item = new Roster.Item(); item.jid = new Jid(row[db.roster.jid]); item.name = row[db.roster.handle]; item.subscription = row[db.roster.subscription]; items[item.jid] = item; } catch (InvalidJidError e) { warning("Ignoring roster entry with invalid Jid: %s", e.message); } } } public string? get_roster_version() { return account.roster_version; } public Collection get_roster() { return items.values; } public Roster.Item? get_item(Jid jid) { return items.has_key(jid) ? items[jid] : null; } public void set_roster_version(string version) { account.roster_version = version; } public void set_roster(Collection items) { db.roster.delete().with(db.roster.account_id, "=", account.id).perform(); foreach (Roster.Item item in items) { set_item(item); } } public void set_item(Roster.Item item) { items[item.jid] = item; db.roster.upsert() .value(db.roster.account_id, account.id, true) .value(db.roster.jid, item.jid.to_string(), true) .value(db.roster.handle, item.name) .value(db.roster.subscription, item.subscription) .perform(); } public void remove_item(Roster.Item item) { items.unset(item.jid); db.roster.delete() .with(db.roster.account_id, "=", account.id) .with(db.roster.jid, "=", item.jid.to_string()).perform(); } } } dino-0.4.3/libdino/src/service/search_processor.vala0000644000000000000000000003553514452563620021247 0ustar rootrootusing Gee; using Xmpp; using Qlite; using Dino.Entities; namespace Dino { public class SearchProcessor : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("search_processor"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { SearchProcessor m = new SearchProcessor(stream_interactor, db); stream_interactor.add_module(m); } public SearchProcessor(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } private QueryBuilder prepare_search(string query, bool join_content) { string words = ""; string? with = null; string? in_ = null; string? from = null; foreach(string word in query.split(" ")) { if (word.has_prefix("with:")) { if (with == null) { @with = word.substring(5); } else { return db.message.select().where("0"); } } else if (word.has_prefix("in:")) { if (in_ == null) { in_ = word.substring(3); } else { return db.message.select().where("0"); } } else if (word.has_prefix("from:")) { if (from == null) { from = word.substring(5); } else { return db.message.select().where("0"); } } else { words += word + "* "; } } if (in_ != null && with != null) { return db.message.select().where("0"); } QueryBuilder rows = db.message .match(db.message.body, words) .order_by(db.message.id, "DESC") .join_with(db.jid, db.jid.id, db.message.counterpart_id) .join_with(db.account, db.account.id, db.message.account_id) .outer_join_with(db.real_jid, db.real_jid.message_id, db.message.id) .with(db.account.enabled, "=", true); if (join_content) { rows.join_on(db.content_item, "message.id=content_item.foreign_id AND content_item.content_type=1") .with(db.content_item.content_type, "=", 1); } if (with != null) { if (with.index_of("/") > 0) { rows.with(db.message.type_, "=", Message.Type.GROUPCHAT_PM) .with(db.jid.bare_jid, "LIKE", with.substring(0, with.index_of("/"))) .with(db.message.counterpart_resource, "LIKE", with.substring(with.index_of("/") + 1)); } else { rows.where(@"($(db.message.type_) = $((int)Message.Type.CHAT) AND $(db.jid.bare_jid) LIKE ?)" + @" OR ($(db.message.type_) = $((int)Message.Type.GROUPCHAT_PM) AND $(db.real_jid.real_jid) LIKE ?)" + @" OR ($(db.message.type_) = $((int)Message.Type.GROUPCHAT_PM) AND $(db.message.counterpart_resource) LIKE ?)", {with, with, with}); } } else if (in_ != null) { rows.with(db.jid.bare_jid, "LIKE", in_) .with(db.message.type_, "=", Message.Type.GROUPCHAT); } if (from != null) { rows.where(@"($(db.message.direction) = 1 AND $(db.account.bare_jid) LIKE ?)" + @" OR ($(db.message.direction) = 1 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.message.our_resource) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) = $((int)Message.Type.CHAT) AND $(db.jid.bare_jid) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.real_jid.real_jid) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.message.counterpart_resource) LIKE ?)", {from, from, from, from, from}); } return rows; } public Gee.List suggest_auto_complete(string query, int cursor_position, int limit = 5) { int after_prev_space = query.substring(0, cursor_position).last_index_of(" ") + 1; int next_space = query.index_of(" ", after_prev_space); if (next_space < 0) next_space = query.length; string current_query = query.substring(after_prev_space, next_space - after_prev_space); Gee.List suggestions = new ArrayList(); if (current_query.has_prefix("from:")) { if (cursor_position < after_prev_space + 5) return suggestions; string current_from = current_query.substring(5); string[] splitted = query.split(" "); foreach(string s in splitted) { if (s.has_prefix("from:") && s != "from:" + current_from) { // Already have an from: filter -> no useful autocompletion possible return suggestions; } } string? current_in = null; string? current_with = null; foreach(string s in splitted) { if (s.has_prefix("in:")) { current_in = s.substring(3); } else if (s.has_prefix("with:")) { current_with = s.substring(5); } } if (current_in != null && current_with != null) { // in: and with: -> no useful autocompletion possible return suggestions; } if (current_with != null) { // Can only be the other one or us // Normal chat QueryBuilder chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .with(db.jid.bare_jid, "=", current_with) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.CHAT) .order_by(db.conversation.last_active, "DESC"); foreach(Row chat in chats) { try { if (suggestions.size == 0) { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "from:"+chat[db.jid.bare_jid], after_prev_space, next_space)); } suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.account.bare_jid]), "from:"+chat[db.account.bare_jid], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } return suggestions; } if (current_in != null) { // All members of the MUC with history QueryBuilder msgs = db.message.select() .select_string(@"account.*, $(db.message.counterpart_resource), conversation.*") .join_with(db.jid, db.jid.id, db.message.counterpart_id) .join_with(db.account, db.account.id, db.message.account_id) .join_on(db.conversation, @"$(db.conversation.account_id)=$(db.account.id) AND $(db.conversation.jid_id)=$(db.jid.id)") .with(db.jid.bare_jid, "=", current_in) .with(db.account.enabled, "=", true) .with(db.message.type_, "=", Message.Type.GROUPCHAT) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT) .with(db.message.counterpart_resource, "LIKE", @"%$current_from%") .group_by({db.message.counterpart_resource}) .order_by_name(@"MAX($(db.message.time))", "DESC") .limit(5); foreach(Row msg in msgs) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, msg), new Jid(current_in).with_resource(msg[db.message.counterpart_resource]), "from:"+msg[db.message.counterpart_resource], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } } // TODO: auto complete from } else if (current_query.has_prefix("with:")) { if (cursor_position < after_prev_space + 5) return suggestions; string current_with = current_query.substring(5); string[] splitted = query.split(" "); foreach(string s in splitted) { if ((s.has_prefix("with:") && s != "with:" + current_with) || s.has_prefix("in:")) { // Already have an in: or with: filter -> no useful autocompletion possible return suggestions; } } // Normal chat QueryBuilder chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .outer_join_on(db.roster, @"$(db.jid.bare_jid) = $(db.roster.jid) AND $(db.account.id) = $(db.roster.account_id)") .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.roster.handle) LIKE ?", {@"%$current_with%", @"%$current_with%"}) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.CHAT) .order_by(db.conversation.last_active, "DESC") .limit(limit); foreach(Row chat in chats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "with:"+chat[db.jid.bare_jid], after_prev_space, next_space) { order = chat[db.conversation.last_active]}); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } // Groupchat PM if (suggestions.size < 5) { chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.conversation.resource) LIKE ?", {@"%$current_with%", @"%$current_with%"}) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT_PM) .order_by(db.conversation.last_active, "DESC") .limit(limit - suggestions.size); foreach(Row chat in chats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]).with_resource(chat[db.conversation.resource]), "with:"+chat[db.jid.bare_jid]+"/"+chat[db.conversation.resource], after_prev_space, next_space) { order = chat[db.conversation.last_active]}); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } suggestions.sort((a, b) => (int)(b.order - a.order)); } } else if (current_query.has_prefix("in:")) { if (cursor_position < after_prev_space + 3) return suggestions; string current_in = current_query.substring(3); string[] splitted = query.split(" "); foreach(string s in splitted) { if ((s.has_prefix("in:") && s != "in:" + current_in) || s.has_prefix("with:")) { // Already have an in: or with: filter -> no useful autocompletion possible return suggestions; } } QueryBuilder groupchats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .with(db.jid.bare_jid, "LIKE", @"%$current_in%") .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT) .order_by(db.conversation.last_active, "DESC") .limit(limit); foreach(Row chat in groupchats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "in:"+chat[db.jid.bare_jid], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } } else { // Other auto complete? } return suggestions; } public Gee.List match_messages(string query, int offset = -1) { Gee.List ret = new ArrayList(); QueryBuilder rows = prepare_search(query, true).limit(10); if (offset > 0) { rows.offset(offset); } foreach (Row row in rows) { try { Message message = new Message.from_row(db, row); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(message); ret.add(new MessageItem(message, conversation, row[db.content_item.id])); } catch (InvalidJidError e) { warning("Ignoring search result with invalid Jid: %s", e.message); } } return ret; } public int count_match_messages(string query) { return (int)prepare_search(query, false).select({db.message.id}).count(); } } public class SearchSuggestion : Object { public Account account { get { return conversation.account; } } public Conversation conversation { get; private set; } public Jid? jid { get; private set; } public string completion { get; private set; } public int start_index { get; private set; } public int end_index { get; private set; } public long order { get; set; } public SearchSuggestion(Conversation conversation, Jid? jid, string completion, int start_index, int end_index) { this.conversation = conversation; this.jid = jid; this.completion = completion; this.start_index = start_index; this.end_index = end_index; } } } dino-0.4.3/libdino/src/service/stream_interactor.vala0000644000000000000000000000631114452563620021416 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class StreamInteractor : Object { public signal void account_added(Account account); public signal void account_removed(Account account); public signal void stream_resumed(Account account, XmppStream stream); public signal void stream_negotiated(Account account, XmppStream stream); public signal void stream_attached_modules(Account account, XmppStream stream); public ModuleManager module_manager; public ConnectionManager connection_manager; private ArrayList modules = new ArrayList(); public StreamInteractor(Database db) { module_manager = new ModuleManager(); connection_manager = new ConnectionManager(module_manager); connection_manager.stream_opened.connect(on_stream_opened); connection_manager.stream_attached_modules.connect((account, stream) => { stream_attached_modules(account, stream); }); } public void connect_account(Account account) { module_manager.initialize(account); account_added(account); connection_manager.connect_account(account); } public async void disconnect_account(Account account) { yield connection_manager.disconnect_account(account); account_removed(account); } public ArrayList get_accounts() { ArrayList ret = new ArrayList(Account.equals_func); foreach (Account account in connection_manager.get_managed_accounts()) { ret.add(account); } return ret; } public XmppStream? get_stream(Account account) { return connection_manager.get_stream(account); } public void add_module(StreamInteractionModule module) { modules.add(module); } public T? get_module(ModuleIdentity? identity) { if (identity == null) return null; foreach (StreamInteractionModule module in modules) { if (identity.matches(module)) return identity.cast(module); } return null; } public new T? get() { foreach (StreamInteractionModule module in modules) { if (module.get_type() == typeof(T)) return (T?) module; } return null; } private void on_stream_opened(Account account, XmppStream stream) { stream.stream_negotiated.connect( (stream) => { var flag = stream.get_flag(Xep.StreamManagement.Flag.IDENTITY); if (flag == null || flag.resumed == false) { stream_negotiated(account, stream); } else if (flag != null && flag.resumed == true) { stream_resumed(account, stream); } }); } } public class ModuleIdentity : Object { public string id { get; private set; } public ModuleIdentity(string id) { this.id = id; } public T? cast(StreamInteractionModule module) { return module.get_type().is_a(typeof(T)) ? (T?) module : null; } public bool matches(StreamInteractionModule module) { return module.id == id; } } public interface StreamInteractionModule : Object { public abstract string id { get; } } } dino-0.4.3/libdino/src/service/util.vala0000644000000000000000000000207714452563620016653 0ustar rootrootusing Dino.Entities; using Qlite; namespace Dino { public class Util { public static Message.Type get_message_type_for_conversation(Conversation conversation) { switch (conversation.type_) { case Conversation.Type.CHAT: return Entities.Message.Type.CHAT; case Conversation.Type.GROUPCHAT: return Entities.Message.Type.GROUPCHAT; case Conversation.Type.GROUPCHAT_PM: return Entities.Message.Type.GROUPCHAT_PM; default: assert_not_reached(); } } public static Conversation.Type get_conversation_type_for_message(Message message) { switch (message.type_) { case Entities.Message.Type.CHAT: return Conversation.Type.CHAT; case Entities.Message.Type.GROUPCHAT: return Conversation.Type.GROUPCHAT; case Entities.Message.Type.GROUPCHAT_PM: return Conversation.Type.GROUPCHAT_PM; default: assert_not_reached(); } } } } dino-0.4.3/libdino/src/util/0000755000000000000000000000000014452563620014340 5ustar rootrootdino-0.4.3/libdino/src/util/display_name.vala0000644000000000000000000001265314452563620017661 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public static string get_conversation_display_name(StreamInteractor stream_interactor, Conversation conversation, string? muc_pm_format) { if (conversation.type_ == Conversation.Type.CHAT) { string? display_name = get_real_display_name(stream_interactor, conversation.account, conversation.counterpart); if (display_name != null) return display_name; return conversation.counterpart.to_string(); } if (conversation.type_ == Conversation.Type.GROUPCHAT) { return get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart); } if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { return (muc_pm_format ?? "%s / %s").printf(get_occupant_display_name(stream_interactor, conversation, conversation.counterpart), get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart.bare_jid)); } return conversation.counterpart.to_string(); } public static string get_participant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid participant, string? self_word = null) { if (conversation.type_ == Conversation.Type.CHAT) { return get_real_display_name(stream_interactor, conversation.account, participant, self_word) ?? participant.bare_jid.to_string(); } if ((conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM)) { return get_occupant_display_name(stream_interactor, conversation, participant); } return participant.bare_jid.to_string(); } public static string? get_real_display_name(StreamInteractor stream_interactor, Account account, Jid jid, string? self_word = null) { if (jid.equals_bare(account.bare_jid)) { if (self_word != null && (account.alias == null || account.alias.length == 0)) { return self_word; } if (account.alias != null && account.alias.length == 0) return null; return account.alias; } Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid); if (roster_item != null && roster_item.name != null && roster_item.name != "") { return roster_item.name; } return null; } public static string get_groupchat_display_name(StreamInteractor stream_interactor, Account account, Jid jid) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); string? room_name = muc_manager.get_room_name(account, jid); if (room_name != null && room_name != jid.localpart) { return room_name; } if (muc_manager.is_private_room(account, jid)) { Gee.List? other_occupants = muc_manager.get_other_offline_members(jid, account); if (other_occupants != null && other_occupants.size > 0) { var builder = new StringBuilder (); foreach(Jid occupant in other_occupants) { if (builder.len != 0) { builder.append(", "); } builder.append((get_real_display_name(stream_interactor, account, occupant) ?? occupant.localpart ?? occupant.domainpart).split(" ")[0]); } return builder.str; } } return jid.to_string(); } public static string get_occupant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid jid, string? self_word = null, bool muc_real_name = false) { if (muc_real_name) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); if (muc_manager.is_private_room(conversation.account, conversation.counterpart)) { Jid? real_jid = null; if (jid.equals_bare(conversation.counterpart)) { muc_manager.get_real_jid(jid, conversation.account); } else { real_jid = jid; } if (real_jid != null) { string? display_name = get_real_display_name(stream_interactor, conversation.account, real_jid, self_word); if (display_name != null) return display_name; } } } // If it's us (jid=our real full JID), display our nick if (conversation.type_ == Conversation.Type.GROUPCHAT_PM && conversation.account.bare_jid.equals_bare(jid)) { var muc_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conversation.counterpart.bare_jid, conversation.account, Conversation.Type.GROUPCHAT); if (muc_conv != null && muc_conv.nickname != null) { return muc_conv.nickname; } } // If it's someone else's real jid, recover nickname if (!jid.equals_bare(conversation.counterpart)) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); Jid? occupant_jid = muc_manager.get_occupant_jid(conversation.account, conversation.counterpart.bare_jid, jid); if (occupant_jid != null && occupant_jid.resourcepart != null) { return occupant_jid.resourcepart; } } return jid.resourcepart ?? jid.to_string(); } }dino-0.4.3/libdino/src/util/util.vala0000644000000000000000000000565714452563620016177 0ustar rootrootnamespace Dino { private extern const string SYSTEM_LIBDIR_NAME; private extern const string SYSTEM_PLUGIN_DIR; public class SearchPathGenerator { public string? exec_path { get; private set; } public SearchPathGenerator(string? exec_path) { this.exec_path = exec_path; } public string get_locale_path(string gettext_package, string locale_install_dir) { string? locale_dir = null; if (Path.get_dirname(exec_path).contains("dino") || Path.get_dirname(exec_path) == "." || Path.get_dirname(exec_path).contains("build")) { string exec_locale = Path.build_filename(Path.get_dirname(exec_path), "locale"); if (FileUtils.test(Path.build_filename(exec_locale, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) { locale_dir = exec_locale; } } return locale_dir ?? locale_install_dir; } public string[] get_plugin_paths() { string[] search_paths = new string[0]; if (Environment.get_variable("DINO_PLUGIN_DIR") != null) { search_paths += Environment.get_variable("DINO_PLUGIN_DIR"); } search_paths += Path.build_filename(Environment.get_home_dir(), ".local", "lib", "dino", "plugins"); string? exec_path = this.exec_path; if (exec_path != null) { if (!exec_path.contains(Path.DIR_SEPARATOR_S)) { exec_path = Environment.find_program_in_path(this.exec_path); } if (Path.get_dirname(exec_path).contains("dino") || Path.get_dirname(exec_path) == "." || Path.get_dirname(exec_path).contains("build")) { search_paths += Path.build_filename(Path.get_dirname(exec_path), "plugins"); } if (Path.get_basename(Path.get_dirname(exec_path)) == "bin") { search_paths += Path.build_filename(Path.get_dirname(Path.get_dirname(exec_path)), SYSTEM_LIBDIR_NAME, "dino", "plugins"); } } search_paths += SYSTEM_PLUGIN_DIR; return search_paths; } } public static string get_storage_dir() { return Path.build_filename(Environment.get_user_data_dir(), "dino"); } [CCode (cname = "dino_gettext", cheader_filename = "dino_i18n.h")] public static extern unowned string _(string s); [CCode (cname = "dino_ngettext", cheader_filename = "dino_i18n.h")] public static extern unowned string n(string msgid, string plural, ulong number); [CCode (cname = "bindtextdomain", cheader_filename = "libintl.h")] private static extern unowned string? bindtextdomain(string domainname, string? dirname); [CCode (cname = "bind_textdomain_codeset", cheader_filename = "libintl.h")] private static extern unowned string? bind_textdomain_codeset(string domainname, string? codeset); public static void internationalize(string gettext_package, string locales_dir) { Intl.bind_textdomain_codeset(gettext_package, "UTF-8"); Intl.bindtextdomain(gettext_package, locales_dir); } } dino-0.4.3/libdino/src/util/weak_map.vala0000644000000000000000000000743514452563620017002 0ustar rootrootusing Gee; public class WeakMap : Gee.AbstractMap { private HashMap hash_map; private HashMap notify_map; public HashDataFunc? key_hash_func = null; public EqualDataFunc? key_equal_func = null; public EqualDataFunc? value_equal_func = null; public WeakMap(owned HashDataFunc? key_hash_func = null, owned EqualDataFunc? key_equal_func = null, owned EqualDataFunc? value_equal_func = null) { if (!typeof(V).is_object()) { error("WeakMap only takes values that are Objects"); } this.key_hash_func = (owned)key_hash_func; this.key_equal_func = (owned)key_equal_func; this.value_equal_func = (owned)value_equal_func; if (this.key_equal_func == null || this.key_equal_func == null || this.value_equal_func == null) { hash_map = new HashMap(); notify_map = new HashMap(); } else { hash_map = new HashMap((v) => { return this.key_hash_func != null ? this.key_hash_func(v) : 0; }, (a, b) => { return this.key_equal_func != null ? this.key_equal_func(a, b) : a == b; }, (a, b) => { return this.value_equal_func != null ? this.value_equal_func(a, b) : a == b; }); notify_map = new HashMap((v) => { return this.key_hash_func != null ? this.key_hash_func(v) : 0; }, (a, b) => { return this.key_equal_func != null ? this.key_equal_func(a, b) : a == b; }); } } public override void clear() { foreach (K key in notify_map.keys) { Object o = (Object) hash_map[key]; o.weak_unref(notify_map[key].func); } hash_map.clear(); notify_map.clear(); } public override V @get(K key) { if (!hash_map.has_key(key)) return null; var v = hash_map[key]; return (owned) v; } public override bool has(K key, V value) { return has_key(key) && (this.value_equal_func != null ? this.value_equal_func(hash_map[key], value) : hash_map[key] == value); } public override bool has_key(K key) { return hash_map.has_key(key); } public override Gee.MapIterator map_iterator() { assert_not_reached(); } public override void @set(K key, V value) { assert(value != null); unset(key); Object v_obj = (Object) value; var notify_wrap = new WeakNotifyWrapper((obj) => { hash_map.unset(key); notify_map.unset(key); }); notify_map[key] = notify_wrap; v_obj.weak_ref(notify_wrap.func); hash_map[key] = value; } public override bool unset(K key, out V value = null) { if (!hash_map.has_key(key)) { value = null; return false; } Object v_obj = (Object) hash_map[key]; v_obj.weak_unref(notify_map[key].func); notify_map.unset(key); return hash_map.unset(key, out value); } public override Gee.Set> entries { owned get; } public override Gee.Set keys { owned get { return hash_map.keys; } } public override bool read_only { get { assert_not_reached(); } } public override int size { get { return hash_map.size; } } public override Gee.Collection values { owned get { assert_not_reached(); } } public override void dispose() { foreach (K key in notify_map.keys) { Object o = (Object) hash_map[key]; o.weak_unref(notify_map[key].func); } } } internal class WeakNotifyWrapper { public WeakNotify func; public WeakNotifyWrapper(owned WeakNotify func) { this.func = (owned) func; } }dino-0.4.3/libdino/tests/0000755000000000000000000000000014452563620013736 5ustar rootrootdino-0.4.3/libdino/tests/common.vala0000644000000000000000000000373714452563620016105 0ustar rootrootnamespace Dino.Test { int main(string[] args) { GLib.Test.init(ref args); GLib.Test.set_nonfatal_assertions(); TestSuite.get_root().add_suite(new WeakMapTest().get_suite()); return GLib.Test.run(); } bool fail_if(bool exp, string? reason = null) { if (exp) { if (reason != null) GLib.Test.message(reason); GLib.Test.fail(); return true; } return false; } void fail_if_reached(string? reason = null) { fail_if(true, reason); } delegate void ErrorFunc() throws Error; void fail_if_not_error_code(ErrorFunc func, int expectedCode, string? reason = null) { try { func(); fail_if_reached(@"$(reason + ": " ?? "")no error thrown"); } catch (Error e) { fail_if_not_eq_int(e.code, expectedCode, @"$(reason + ": " ?? "")catched unexpected error"); } } bool fail_if_not(bool exp, string? reason = null) { return fail_if(!exp, reason); } bool fail_if_eq_int(int left, int right, string? reason = null) { return fail_if(left == right, @"$(reason + ": " ?? "")$left == $right"); } bool fail_if_not_eq_int(int left, int right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_str(string left, string right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_uint8_arr(uint8[] left, uint8[] right, string? reason = null) { if (fail_if_not_eq_int(left.length, right.length, @"$(reason + ": " ?? "")array length not equal")) return true; return fail_if_not_eq_str(Base64.encode(left), Base64.encode(right), reason); } bool fail_if_not_zero_int(int zero, string? reason = null) { return fail_if_not_eq_int(zero, 0, reason); } bool fail_if_zero_int(int zero, string? reason = null) { return fail_if_eq_int(zero, 0, reason); } bool fail_if_null(void* what, string? reason = null) { return fail_if(what == null || (size_t)what == 0, reason); } } dino-0.4.3/libdino/tests/jid.vala0000644000000000000000000000221514452563620015351 0ustar rootrootusing Dino.Entities; namespace Dino.Test { class JidTest : Gee.TestCase { public JidTest() { base("Jid"); add_test("parse", test_parse); add_test("components", test_components); add_test("with_res", test_with_res); } private void test_parse() { Jid jid = new Jid("user@example.com/res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } private void test_components() { Jid jid = new Jid.components("user", "example.com", "res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } private void test_with_res() { Jid jid = new Jid.with_resource("user@example.com", "res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } } } dino-0.4.3/libdino/tests/testcase.vala0000644000000000000000000000451314452563620016421 0ustar rootroot/* testcase.vala * * Copyright (C) 2009 Julien Peeters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Julien Peeters */ public abstract class Gee.TestCase : Object { private GLib.TestSuite suite; private Adaptor[] adaptors = new Adaptor[0]; public delegate void TestMethod (); protected TestCase (string name) { this.suite = new GLib.TestSuite (name); } public void add_test (string name, owned TestMethod test) { var adaptor = new Adaptor (name, (owned)test, this); this.adaptors += adaptor; this.suite.add (new GLib.TestCase (adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down )); } public virtual void set_up () { } public virtual void tear_down () { } public GLib.TestSuite get_suite () { return (owned) this.suite; } private class Adaptor { [CCode (notify = false)] public string name { get; private set; } private TestMethod test; private TestCase test_case; public Adaptor (string name, owned TestMethod test, TestCase test_case) { this.name = name; this.test = (owned)test; this.test_case = test_case; } public void set_up (void* fixture) { this.test_case.set_up (); } public void run (void* fixture) { this.test (); } public void tear_down (void* fixture) { this.test_case.tear_down (); } } } dino-0.4.3/libdino/tests/weak_map.vala0000644000000000000000000000475614452563620016403 0ustar rootrootusing Dino.Entities; namespace Dino.Test { class WeakMapTest : Gee.TestCase { public WeakMapTest() { base("WeakMapTest"); add_test("set", test_set); add_test("set2", test_set2); add_test("set3", test_set3); add_test("set4", test_unset); add_test("remove_when_out_of_scope", test_remove_when_out_of_scope); // add_test("non_object_construction", test_non_object_construction); } private void test_set() { WeakMap map = new WeakMap(); var o = new Object(); map[1] = o; assert(map.size == 1); assert(map.has_key(1)); assert(map[1] == o); } private void test_set2() { WeakMap map = new WeakMap(); var o = new Object(); var o2 = new Object(); map[1] = o; map[1] = o2; assert(map.size == 1); assert(map.has_key(1)); assert(map[1] == o2); } private void test_set3() { WeakMap map = new WeakMap(); var o1 = new Object(); var o2 = new Object(); map[0] = o1; map[3] = o2; { var o3 = new Object(); var o4 = new Object(); map[7] = o3; map[50] = o4; } var o5 = new Object(); map[5] = o5; assert(map.size == 3); assert(map.has_key(0)); assert(map.has_key(3)); assert(map.has_key(5)); assert(map[0] == o1); assert(map[3] == o2); assert(map[5] == o5); } private void test_unset() { WeakMap map = new WeakMap(); var o1 = new Object(); map[7] = o1; map.unset(7); assert_true(map.size == 0); assert_true(map.is_empty); assert_false(map.has_key(7)); } private void test_remove_when_out_of_scope() { WeakMap map = new WeakMap(); { map[0] = new Object(); } assert_false(map.has_key(0)); } private void test_non_object_construction() { WeakMap map = new WeakMap(); assert_not_reached(); } } } dino-0.4.3/main/0000755000000000000000000000000014452563620012100 5ustar rootrootdino-0.4.3/main/CMakeLists.txt0000644000000000000000000002136014452563620014642 0ustar rootrootset(GETTEXT_PACKAGE "dino") find_package(Gettext) include(${GETTEXT_USE_FILE}) gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/po TARGET_NAME ${GETTEXT_PACKAGE}-translations) find_package(GTK4 REQUIRED) find_packages(MAIN_PACKAGES REQUIRED Gee GLib GModule GObject GTK4 ICU Adwaita ) set(RESOURCE_LIST dino-conversation-list-placeholder-arrow.svg icons/scalable/actions/dino-account-plus-symbolic.svg icons/scalable/actions/dino-emoticon-add-symbolic.svg icons/scalable/actions/dino-emoticon-symbolic.svg icons/scalable/actions/dino-qr-code-symbolic.svg icons/scalable/apps/im.dino.Dino.svg icons/scalable/apps/im.dino.Dino-symbolic.svg icons/scalable/devices/dino-device-desktop-symbolic.svg icons/scalable/devices/dino-device-phone-symbolic.svg icons/scalable/devices/dino-phone-hangup-symbolic.svg icons/scalable/devices/dino-phone-in-talk-symbolic.svg icons/scalable/devices/dino-phone-missed-symbolic.svg icons/scalable/devices/dino-phone-ring-symbolic.svg icons/scalable/devices/dino-phone-symbolic.svg icons/scalable/mimetypes/dino-file-document-symbolic.svg icons/scalable/mimetypes/dino-file-download-symbolic.svg icons/scalable/mimetypes/dino-file-image-symbolic.svg icons/scalable/mimetypes/dino-file-music-symbolic.svg icons/scalable/mimetypes/dino-file-symbolic.svg icons/scalable/mimetypes/dino-file-table-symbolic.svg icons/scalable/mimetypes/dino-file-video-symbolic.svg icons/scalable/status/dino-double-tick-symbolic.svg icons/scalable/status/dino-microphone-off-symbolic.svg icons/scalable/status/dino-microphone-symbolic.svg icons/scalable/status/dino-party-popper-symbolic.svg icons/scalable/status/dino-security-high-symbolic.svg icons/scalable/status/dino-status-away.svg icons/scalable/status/dino-status-chat.svg icons/scalable/status/dino-status-dnd.svg icons/scalable/status/dino-status-online.svg icons/scalable/status/dino-tick-symbolic.svg icons/scalable/status/dino-video-off-symbolic.svg icons/scalable/status/dino-video-symbolic.svg add_conversation/add_contact_dialog.ui add_conversation/add_groupchat_dialog.ui add_conversation/conference_details_fragment.ui add_conversation/list_row.ui add_conversation/select_jid_fragment.ui call_widget.ui chat_input.ui contact_details_dialog.ui conversation_item_widget.ui conversation_list_titlebar.ui conversation_list_titlebar_csd.ui conversation_row.ui conversation_view.ui file_default_widget.ui file_send_overlay.ui global_search.ui conversation_content_view/item_metadata_header.ui conversation_content_view/view.ui manage_accounts/account_row.ui manage_accounts/add_account_dialog.ui manage_accounts/dialog.ui menu_add.ui menu_app.ui menu_conversation.ui menu_encryption.ui message_item_widget_edit_mode.ui occupant_list.ui occupant_list_item.ui quote.ui search_autocomplete.ui settings_dialog.ui shortcuts.ui unified_main_content.ui unified_window_placeholder.ui style.css style-dark.css ) compile_gresources( MAIN_GRESOURCES_TARGET MAIN_GRESOURCES_XML TARGET ${CMAKE_CURRENT_BINARY_DIR}/resources/resources.c TYPE EMBED_C RESOURCES ${RESOURCE_LIST} PREFIX /im/dino/Dino SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data ) unset(MAIN_EXTRA_OPTIONS) unset(MAIN_EXTRA_PACKAGES) set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) set(MAIN_DEFINITIONS) if(GTK4_VERSION VERSION_GREATER_EQUAL "4.6") set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} GTK_4_6) endif() if(GTK4_VERSION VERSION_GREATER_EQUAL "4.8") set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} GTK_4_8) endif() if(Adwaita_VERSION VERSION_GREATER_EQUAL "1.2") set(MAIN_DEFINITIONS ${MAIN_DEFINITIONS} Adw_1_2) endif() vala_precompile(MAIN_VALA_C SOURCES src/main.vala src/ui/application.vala src/ui/avatar_drawer.vala src/ui/avatar_image.vala src/ui/conversation_list_titlebar.vala src/ui/conversation_view.vala src/ui/conversation_view_controller.vala src/ui/file_send_overlay.vala src/ui/global_search.vala src/ui/notifier_freedesktop.vala src/ui/notifier_gnotifications.vala src/ui/settings_dialog.vala src/ui/main_window.vala src/ui/main_window_controller.vala src/ui/add_conversation/add_conference_dialog.vala src/ui/add_conversation/add_contact_dialog.vala src/ui/add_conversation/add_groupchat_dialog.vala src/ui/add_conversation/conference_details_fragment.vala src/ui/add_conversation/conference_list.vala src/ui/add_conversation/list_row.vala src/ui/add_conversation/roster_list.vala src/ui/add_conversation/select_contact_dialog.vala src/ui/add_conversation/select_jid_fragment.vala src/ui/call_window/audio_settings_popover.vala src/ui/call_window/call_bottom_bar.vala src/ui/call_window/call_connection_details_window.vala src/ui/call_window/call_encryption_button.vala src/ui/call_window/call_window.vala src/ui/call_window/call_window_controller.vala src/ui/call_window/participant_widget.vala src/ui/call_window/video_settings_popover.vala src/ui/conversation_content_view/call_widget.vala src/ui/conversation_content_view/chat_state_populator.vala src/ui/conversation_content_view/content_populator.vala src/ui/conversation_content_view/conversation_item_skeleton.vala src/ui/conversation_content_view/conversation_view.vala src/ui/conversation_content_view/date_separator_populator.vala src/ui/conversation_content_view/file_default_widget.vala src/ui/conversation_content_view/file_image_widget.vala src/ui/conversation_content_view/file_widget.vala src/ui/conversation_content_view/item_actions.vala src/ui/conversation_content_view/message_widget.vala src/ui/conversation_content_view/quote_widget.vala src/ui/conversation_content_view/reactions_widget.vala src/ui/conversation_content_view/subscription_notification.vala src/ui/chat_input/chat_input_controller.vala src/ui/chat_input/chat_text_view.vala src/ui/chat_input/encryption_button.vala src/ui/chat_input/occupants_tab_completer.vala src/ui/chat_input/smiley_converter.vala src/ui/chat_input/view.vala src/ui/contact_details/blocking_provider.vala src/ui/contact_details/settings_provider.vala src/ui/contact_details/permissions_provider.vala src/ui/contact_details/dialog.vala src/ui/contact_details/muc_config_form_provider.vala src/ui/conversation_selector/conversation_selector.vala src/ui/conversation_selector/conversation_selector_row.vala src/ui/conversation_titlebar/call_entry.vala src/ui/conversation_titlebar/menu_entry.vala src/ui/conversation_titlebar/occupants_entry.vala src/ui/conversation_titlebar/search_entry.vala src/ui/conversation_titlebar/conversation_titlebar.vala src/ui/manage_accounts/account_row.vala src/ui/manage_accounts/add_account_dialog.vala src/ui/manage_accounts/dialog.vala src/ui/occupant_menu/list.vala src/ui/occupant_menu/list_row.vala src/ui/occupant_menu/view.vala src/ui/util/accounts_combo_box.vala src/ui/util/config.vala src/ui/util/data_forms.vala src/ui/util/helper.vala src/ui/util/label_hybrid.vala src/ui/util/sizing_bin.vala src/ui/util/size_request_box.vala src/ui/widgets/date_separator.vala src/ui/widgets/fixed_ratio_picture.vala src/ui/widgets/natural_size_increase.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/dino_internal.vapi PACKAGES ${MAIN_PACKAGES} ${MAIN_EXTRA_PACKAGES} GRESOURCES ${MAIN_GRESOURCES_XML} DEFINITIONS ${MAIN_DEFINITIONS} OPTIONS ${MAIN_EXTRA_OPTIONS} ) add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="dino") add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET}) add_dependencies(dino ${GETTEXT_PACKAGE}-translations) target_include_directories(dino PRIVATE src) target_link_libraries(dino libdino ${MAIN_PACKAGES}) if(WIN32) target_link_libraries(dino -mwindows) endif(WIN32) install(TARGETS dino ${TARGET_INSTALL}) install(FILES data/im.dino.Dino.appdata.xml DESTINATION ${APPDATA_FILE_INSTALL_DIR}) install(FILES data/im.dino.Dino.desktop DESTINATION ${DESKTOP_FILE_INSTALL_DIR}) install(FILES data/im.dino.Dino.service DESTINATION ${SERVICE_FILE_INSTALL_DIR}) install(FILES data/icons/scalable/apps/im.dino.Dino.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/scalable/apps) install(FILES data/icons/scalable/apps/im.dino.Dino-symbolic.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/symbolic/apps) dino-0.4.3/main/data/0000755000000000000000000000000014452563620013011 5ustar rootrootdino-0.4.3/main/data/add_conversation/0000755000000000000000000000000014452563620016333 5ustar rootrootdino-0.4.3/main/data/add_conversation/add_contact_dialog.ui0000644000000000000000000001264214452563620022461 0ustar rootroot dino-0.4.3/main/data/add_conversation/add_groupchat_dialog.ui0000644000000000000000000002200614452563620023015 0ustar rootroot dino-0.4.3/main/data/add_conversation/conference_details_fragment.ui0000644000000000000000000003502314452563620024374 0ustar rootroot dino-0.4.3/main/data/add_conversation/list_row.ui0000644000000000000000000000440714452563620020541 0ustar rootroot 3 3 3 3 10 False 30 30 center center vertical 1 end 1 0 0 0 1 end 1 0 0 1 dino-0.4.3/main/data/add_conversation/select_jid_fragment.ui0000644000000000000000000001005114452563620022657 0ustar rootroot dino-0.4.3/main/data/call_widget.ui0000644000000000000000000001504714452563620015635 0ustar rootroot dino-0.4.3/main/data/chat_input.ui0000644000000000000000000001161114452563620015506 0ustar rootroot dino-0.4.3/main/data/contact_details_dialog.ui0000644000000000000000000001552214452563620020034 0ustar rootroot dino-0.4.3/main/data/conversation_content_view/0000755000000000000000000000000014452563620020307 5ustar rootrootdino-0.4.3/main/data/conversation_content_view/item_metadata_header.ui0000644000000000000000000000211214452563620024750 0ustar rootroot dino-0.4.3/main/data/conversation_content_view/view.ui0000644000000000000000000000774314452563620021633 0ustar rootroot dino-0.4.3/main/data/conversation_item_widget.ui0000644000000000000000000000466314452563620020454 0ustar rootroot 7 2 35 35 start 2 0 0 2 end 0 baseline 1 0 0 baseline 2 0 0.4 12 2 3 0 0.4 13 2 start True 4 0 dino-0.4.3/main/data/conversation_list_titlebar.ui0000644000000000000000000000354614452563620021013 0ustar rootroot dino-0.4.3/main/data/conversation_list_titlebar_csd.ui0000644000000000000000000000232614452563620021637 0ustar rootroot False False list-add-symbolic normal False open-menu-symbolic normal dino-0.4.3/main/data/conversation_row.ui0000644000000000000000000002675414452563620016767 0ustar rootroot dino-0.4.3/main/data/conversation_view.ui0000644000000000000000000001006214452563620017113 0ustar rootroot dino-0.4.3/main/data/dino-conversation-list-placeholder-arrow.svg0000644000000000000000000000124114452563620023552 0ustar rootroot dino-0.4.3/main/data/file_default_widget.ui0000644000000000000000000000721514452563620017343 0ustar rootroot dino-0.4.3/main/data/file_send_overlay.ui0000644000000000000000000000642714452563620017052 0ustar rootroot 0 0 center center vertical 20 20 10 10 Send a file 1 1 window-close-symbolic 15 15 5 Send 1 dino-0.4.3/main/data/global_search.ui0000644000000000000000000001366614452563620016151 0ustar rootroot vertical 12 12 12 12 empty system-search-symbolic No active search Type to start a search no-result face-uncertain-symbolic No matching messages Check the spelling or try to remove filters results vertical 0 1 17 never 1 1 vertical 25 10 10 10 10 start 42 12 12 start browse dino-0.4.3/main/data/icons/0000755000000000000000000000000014452563620014124 5ustar rootrootdino-0.4.3/main/data/icons/scalable/0000755000000000000000000000000014452563620015672 5ustar rootrootdino-0.4.3/main/data/icons/scalable/actions/0000755000000000000000000000000014452563620017332 5ustar rootrootdino-0.4.3/main/data/icons/scalable/actions/dino-account-plus-symbolic.svg0000644000000000000000000000071014452563620025234 0ustar rootrootdino-0.4.3/main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg0000644000000000000000000000260214452563620025164 0ustar rootroot dino-0.4.3/main/data/icons/scalable/actions/dino-emoticon-symbolic.svg0000644000000000000000000000112114452563620024431 0ustar rootroot dino-0.4.3/main/data/icons/scalable/actions/dino-qr-code-symbolic.svg0000644000000000000000000000107014452563620024151 0ustar rootroot dino-0.4.3/main/data/icons/scalable/apps/0000755000000000000000000000000014452563620016635 5ustar rootrootdino-0.4.3/main/data/icons/scalable/apps/im.dino.Dino-symbolic.svg0000644000000000000000000000410414452563620023421 0ustar rootroot dino-0.4.3/main/data/icons/scalable/apps/im.dino.Dino.svg0000644000000000000000000001630614452563620021611 0ustar rootroot dino-0.4.3/main/data/icons/scalable/devices/0000755000000000000000000000000014452563620017314 5ustar rootrootdino-0.4.3/main/data/icons/scalable/devices/dino-device-desktop-symbolic.svg0000644000000000000000000000053214452563620025511 0ustar rootroot dino-0.4.3/main/data/icons/scalable/devices/dino-device-phone-symbolic.svg0000644000000000000000000000075214452563620025155 0ustar rootroot dino-0.4.3/main/data/icons/scalable/devices/dino-phone-hangup-symbolic.svg0000644000000000000000000000146014452563620025175 0ustar rootrootdino-0.4.3/main/data/icons/scalable/devices/dino-phone-in-talk-symbolic.svg0000644000000000000000000000123014452563620025245 0ustar rootrootdino-0.4.3/main/data/icons/scalable/devices/dino-phone-missed-symbolic.svg0000644000000000000000000000157214452563620025203 0ustar rootrootdino-0.4.3/main/data/icons/scalable/devices/dino-phone-ring-symbolic.svg0000644000000000000000000000145414452563620024655 0ustar rootrootdino-0.4.3/main/data/icons/scalable/devices/dino-phone-symbolic.svg0000644000000000000000000000107614452563620023720 0ustar rootrootdino-0.4.3/main/data/icons/scalable/mimetypes/0000755000000000000000000000000014452563620017706 5ustar rootrootdino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-document-symbolic.svg0000644000000000000000000000073314452563620025733 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-download-symbolic.svg0000644000000000000000000000071114452563620025720 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-image-symbolic.svg0000644000000000000000000000124314452563620025174 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-music-symbolic.svg0000644000000000000000000000211714452563620025233 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-symbolic.svg0000644000000000000000000000064114452563620024115 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-table-symbolic.svg0000644000000000000000000000112714452563620025202 0ustar rootroot dino-0.4.3/main/data/icons/scalable/mimetypes/dino-file-video-symbolic.svg0000644000000000000000000000123514452563620025221 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/0000755000000000000000000000000014452563620017215 5ustar rootrootdino-0.4.3/main/data/icons/scalable/status/dino-double-tick-symbolic.svg0000644000000000000000000000171714452563620024714 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-microphone-off-symbolic.svg0000644000000000000000000000122514452563620025417 0ustar rootrootdino-0.4.3/main/data/icons/scalable/status/dino-microphone-symbolic.svg0000644000000000000000000000073414452563620024653 0ustar rootrootdino-0.4.3/main/data/icons/scalable/status/dino-party-popper-symbolic.svg0000644000000000000000000001025714452563620025153 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-security-high-symbolic.svg0000644000000000000000000000246414452563620025276 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-status-away.svg0000644000000000000000000000066214452563620023153 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-status-chat.svg0000644000000000000000000000146714452563620023135 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-status-dnd.svg0000644000000000000000000000062114452563620022752 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-status-online.svg0000644000000000000000000000045614452563620023477 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-tick-symbolic.svg0000644000000000000000000000120314452563620023432 0ustar rootroot dino-0.4.3/main/data/icons/scalable/status/dino-video-off-symbolic.svg0000644000000000000000000000070614452563620024365 0ustar rootrootdino-0.4.3/main/data/icons/scalable/status/dino-video-symbolic.svg0000644000000000000000000000061714452563620023616 0ustar rootrootdino-0.4.3/main/data/im.dino.Dino.appdata.xml0000644000000000000000000005650014452563620017377 0ustar rootroot im.dino.Dino im.dino.Dino.desktop CC0-1.0 GPL-3.0+ Dino Modern XMPP Chat Client 現代化的 XMPP 用戶端聊天軟件 现代 XMPP 聊天客户端 Modern XMPP Sohbet İstemcisi Modern XMPP-chattklient Klient Modern Fjalosjesh XMPP Современный XMPP клиент Client XMPP de discuții modern Cliente de Chat XMPP Moderno Moderno cliente de chat XMPP Nowoczesny komunikator XMPP Client XMPP modèrn Een moderne XMPP-chatclient Moderne XMPP-sludreklient Šiuolaikinė XMPP pokalbių kliento programa Modernen XMPP Chat Client 현대 XMPP 채팅 클라이언트 現代的な XMPP チャット クライアント Client di chat moderno per XMPP Nútímalegt XMPP-spjallforrit Un modern client de conversationes XMPP Aplikasi chat XMPP modern Modern XMPP csevegőprogram Cliente moderno para conversas XMPP Client de clavardage XMPP moderne کلاینت نوین گپ XMPP XMPP txat bezero modernoa Un cliente de XMPP moderno Moderna XMPP-Retebabililo Σύγχρονος XMPP Chat Client Modernes XMPP-Chat-Programm Moderní XMPP klient Client de xat XMPP modern تطبيق حديث للدردشة عبر XMPP

Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.

Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。

Dino 是一个现代的开源聊天桌面客户端。它致力于提供一个清爽又可靠的 Jabber/XMPP 体验,同时又保护您的隐私。

Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır.

Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser efter din integritet.

Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj.

Dino - это современный клиент для чатов с открытым исходным кодом, направленный на надёжное и приватное использование Jabber/XMPP на персональных компьютерах.

Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, ținând cont de confidențialitatea dumneavoastră.

Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em oferecer uma experiência Jabber/XMPP transparente e confiável levando em consideração a sua privacidade.

Dino é um moderno chat de código aberto para desktop. Ele é focado em prover uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua privacidade.

Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej obsłudze sieci Jabber/XMPP dbając o twoją prywatność.

Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte vòstra confidencialitat.

Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het achterhoofd.

Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om personvernet.

Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą nepamirštant apie jūsų privatumą.

Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op Privatsphär.

Dino는 데스크탑을 위한 현대 오픈소스 채팅 클라이언트입니다. 깔끔하고 신뢰 할 수 있는 Jabber/XMPP 경험을 개인정보 보호 중시와 함께 제공할 수 있도록 주력하고 있습니다.

Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プライバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供を第一に考えて開発されています。

Dino è un client di chat per il desktop, moderno e open-source. Si concentra nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente la tua privacy.

Dino er nútímalegt spjallforrit og frjáls hugbúnaður fyrir skjáborðið. Það leggur áherslu á að veita hreina og áreiðanlega Jabber/XMPP upplifun með friðhelgi þína í huga.

Dino es un modern cliente de conversationes con fonte apert. It foca se sur provider un nett e fidibil experientie de Jabber/XMPP con un attention a confidentialitá.

Dino adalah aplikasi chat open source modern untuk PC. Menyediakan pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda.

A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, miközben a magánszféra megőrzését is fontosnak tartja.

Dino é un cliente moderno e de código aberto para o escritorio. Orientado a fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e seguranza presentes.

Dino est un client de clavardage libre et moderne pour le bureau. Il se concentre sur la fourniture d’une expérience XMPP simple et fiable tout en ayant toujours à l’esprit votre confidentialité.

Dino on nykyaikainen avoimen lähdekoodin jutteluohjelma työpöydälle. Se keskittyy tarjoamaan selkeän ja luotettavan Jabber/XMPP-kokemuksen unohtamatta yksityisyyttäsi.

دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد.

Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan hartzeaz gain.

Dino es un cliente de mensajería moderno y libre para escritorio y móvil. Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo la privacidad en mente.

Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian privatecon.

Το Dino είναι ένας σύγχρονος πελάτης συνομιλίας ανοιχτού κώδικα για desktop υπολογιστές. Επικεντρώνεται στην παροχή μιας καθαρής και αξιόπιστης εμπειρίας Jabber/XMPP έχοντας παράλληλα υπόψη την προστασία των προσωπικών δεδομένων σας.

Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen Schwerpunkt auf Privatsphäre.

Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na zachování vašeho soukromí.

Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en compte la vostra privacitat.

إنّ Dino برنامج عصري ومفتوح المصدر للدردشة صُمّم لسطح المكتب. ويُركّز علي تقديم تجربة نظيفة وموثوق منها لجابر/XMPP مع أخذ خصوصيتكم بعين الإعتبار.

It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications.

它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已讀回條和正在輸入提示。

它支持 OMEMO 和 OpenPGP 端对端加密并允许配置隐私相关的特性比如已读回执和输入提醒。

OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve "okundu" bilgisi, "yazıyor..." bildirimi gibi gizlilikle alakalı özelliklerin ayarlanabilmesini sağlar.

Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter konfigurering av funktioner med integritetspåverkan som läsbekräftelser och skriftaviseringar.

Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime shtypjeje.

Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет настраивать такие функции, как уведомления о прочтении и наборе сообщений.

Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, și permite configurarea caracteristicilor legate de confidențialitate precum trimiterea notificărilor de primire și tastare.

Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, permite configurar funcionalidades relativas à privacidade como notificações de leitura, recebimento e escrita.

Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar privacidade—características relacionadas às notificações de leitura, recebimento e escrita.

Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o pisaniu czy odczytaniu wiadomości.

Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de configurar de foncionalitats ligadas amb la confidencialitat coma los acusats de lectura e las notificacions d’escritura.

Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-typenmeldingen, in te stellen.

Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og skrivevarsling.

Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie žinučių skaitymą ir rašymą.

Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen.

OMEMO와 OpenPGP를 통한 종단간 암호화를 지원하며 프라이버시와 관련된 읽음 확인이나 입력 알림기능을 설정할 수 있습니다.

OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送信や入力通知などのプライバシー関連の設定も可能です。

Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di configurare le funzioni relative alla privacy come le ricevute di lettura e le notifiche di digitazione.

Það styður dulkóðun frá enda til enda með OMEMO og OpenPGP og gerir kleift að stilla persónuverndartengda eiginleika eins og lestrarkvittanir og innsláttartilkynningar.

It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar sensitiv functiones quam confirmation de lectada e notificationes de tippada.

Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan pengaturan fitur terkait privasi seperti tanda pesan dibaca dan pemberitahuan pengetikan.

Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint például az olvasási visszaigazolást és a gépelési értesítéseket.

Suporta o cifrado de punto-a-punto con OMEMO e OpenPGP e permite configurar trazos orientados á privacidade tales coma confirmación de lectura e notificacións de escritura.

Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et permet de configurer les fonctions liées à la confidentialité telles que les accusés de réception et les notifications d’écriture.

Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja kirjoitusilmoitusten asetusten määrittämisen.

از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و اعلان در حال نوشتن بودن.

Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta idazketa jakinarazpenak bezala.

Soporta cifrado fin-a-fin a través de OMEMO y OpenPGP y permite configurar características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura.

Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado.

Υποστηρίζει κρυπτογράφηση από άκρο σε άκρο με OMEMO και OpenPGP και επιτρέπει την ρύθμιση λειτουργιών που σχετίζονται με το απόρρητο, όπως αποδείξεις ανάγνωσης και ειδοποιήσεις πληκτρολόγησης.

Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und enthält Privatsphäre-Einstellungen zu Lesebestätigungen und Tippbenachrichtigungen.

Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení a oznámení o psaní.

Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar funcionalitats relacionades amb la privacitat com per exemple rebuts de lectura i notificacions d'escriptura.

وهو يدعم التشفير بواسطة OMEMO و OpenPGP يسمح بإعداد ميزات الخصوصية كالإيصالات المقروءة والإخطارات عند الكتابة.

Dino fetches history from the server and synchronizes messages with other devices.

Dino 從伺服器取得訊息並與其他裝置同步。

Dino 从服务器获取消息并和其他设备同步。

Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara senkronize eder.

Dino hämtar historik från servern och synkroniserar meddelanden med andra enheter.

Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje të tjera.

Dino загружает историю с сервера и синхронизирует сообщения с другими устройствами.

Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu celelalte dispozitive.

Dino obtém o histórico do servidor e sincroniza mensagens com outros dispositivos.

Dino obtém o histórico do servidor e sincroniza mensagens com outros aparelhos.

Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi urządzeniami.

Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres periferics.

Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten.

Dino henter historikk fra tjeneren og synkroniserer meldinger med andre enheter.

Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais įrenginiais.

Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat anere Geräter.

Dino는 서버와 타 장치와의 메세지 동기화로부터 기록을 불러옵니다.

Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。

Dino recupera la cronologia dal server e sincronizza i messaggi con gli altri dispositivi.

Dino sækir feril af netþjóni og samstillir skilaboð með öðrum tækjum.

Dino obtene li diarium del servitore e sincronisa missages inter altri apparates.

Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan perangkat lain.

A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az üzeneteket a többi eszközzel.

Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros dispositivos.

Dino récupère l’historique du serveur et synchronise les messages avec les autres clients.

Dino hakee historian palvelimelta ja synkronisoi viestit muiden laitteiden kanssa.

دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی می‌کند.

Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak sinkronizatzen ditu.

Dino recupera el historial de mensajes desde el servidor y sincroniza los mensajes con otros dispositivos.

Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj aparatoj.

Το Dino ανακτά το ιστορικό από τον διακομιστή και συγχρονίζει τα μηνύματα με άλλες συσκευές.

Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit anderen Geräten.

Dino načítá historii ze serveru a synchronizuje zprávy s ostatními zařízeními.

Dino recupera l'historial del servidor i sincronitza els missatges amb altres dispositius.

يقوم Dino بجلب السِجلّ مِن السيرفر ثم يُزامِن الرسائل مع الأجهزة الأخرى.

https://dino.im/img/appdata/2022-02_screenshot-main.png https://dino.im/img/appdata/2022-02_screenshot-call.png https://dino.im/img/appdata/start_chat.png dino Dino Development Team https://dino.im https://github.com/dino/dino/issues https://dino.im/#donate https://hosted.weblate.org/projects/dino/ appstream@dino.im

The 0.4 release adds support for message reactions and replies. Also, Dino is ported to GTK4.

The 0.3 release is all about calls. Dino now supports calls between two or more people!

The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.

The first Dino release! Dino is a secure and open-source application for decentralized messaging.

intense intense
dino-0.4.3/main/data/im.dino.Dino.appdata.xml.in0000644000000000000000000000514414452563620020002 0ustar rootroot im.dino.Dino im.dino.Dino.desktop CC0-1.0 GPL-3.0+ Dino Modern XMPP Chat Client

Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.

It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications.

Dino fetches history from the server and synchronizes messages with other devices.

https://dino.im/img/appdata/2022-02_screenshot-main.png https://dino.im/img/appdata/2022-02_screenshot-call.png https://dino.im/img/appdata/start_chat.png dino Dino Development Team https://dino.im https://github.com/dino/dino/issues https://dino.im/#donate https://hosted.weblate.org/projects/dino/ appstream@dino.im

The 0.4 release adds support for message reactions and replies. Also, Dino is ported to GTK4.

The 0.3 release is all about calls. Dino now supports calls between two or more people!

The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.

The first Dino release! Dino is a secure and open-source application for decentralized messaging.

intense intense
dino-0.4.3/main/data/im.dino.Dino.desktop0000644000000000000000000000052714452563620016635 0ustar rootroot[Desktop Entry] Version=1.0 Name=Dino GenericName=Jabber/XMPP Client Keywords=chat;talk;im;message;xmpp;jabber; Exec=dino %U Icon=im.dino.Dino StartupNotify=true Terminal=false Type=Application Categories=GTK;Network;Chat;InstantMessaging; X-GNOME-UsesNotifications=true MimeType=x-scheme-handler/xmpp; X-Purism-FormFactor=Workstation;Mobile; dino-0.4.3/main/data/im.dino.Dino.service0000644000000000000000000000005414452563620016617 0ustar rootroot[D-BUS Service] Name=im.dino.Dino Exec=dino dino-0.4.3/main/data/manage_accounts/0000755000000000000000000000000014452563620016140 5ustar rootrootdino-0.4.3/main/data/manage_accounts/account_row.ui0000644000000000000000000000254214452563620021025 0ustar rootroot dino-0.4.3/main/data/manage_accounts/add_account_dialog.ui0000644000000000000000000013714314452563620022273 0ustar rootroot dino-0.4.3/main/data/manage_accounts/dialog.ui0000644000000000000000000004250314452563620017742 0ustar rootroot dino-0.4.3/main/data/menu_add.ui0000644000000000000000000000102014452563620015115 0ustar rootroot
app.add_chat Start Conversation app.add_conference Join Channel
dino-0.4.3/main/data/menu_app.ui0000644000000000000000000000164514452563620015162 0ustar rootroot
app.accounts Accounts
app.settings Preferences app.open_shortcuts Keyboard Shortcuts app.about About Dino
dino-0.4.3/main/data/menu_conversation.ui0000644000000000000000000000053214452563620017106 0ustar rootroot
app.contact_details Contact Details
dino-0.4.3/main/data/menu_encryption.ui0000644000000000000000000000171714452563620016574 0ustar rootroot vertical 10 10 10 10 Unencrypted 1 1 dino-0.4.3/main/data/message_item_widget_edit_mode.ui0000644000000000000000000000514614452563620021374 0ustar rootroot dino-0.4.3/main/data/occupant_list.ui0000644000000000000000000000226214452563620016221 0ustar rootroot dino-0.4.3/main/data/occupant_list_item.ui0000644000000000000000000000215614452563620017241 0ustar rootroot 3 7 3 7 10 30 30 1 end 1 0 1 0 dino-0.4.3/main/data/quote.ui0000644000000000000000000000512414452563620014507 0ustar rootroot 5 False 15 15 center 0 0 end start baseline 0 1 0 end start baseline 0 True 2 0 end start 0 0 1 3 window-close-symbolic False center 3 0 2 dino-0.4.3/main/data/search_autocomplete.ui0000644000000000000000000000152214452563620017376 0ustar rootroot horizontal 4 4 6 6 24 24 False end dino-0.4.3/main/data/settings_dialog.ui0000644000000000000000000000762614452563620016542 0ustar rootroot dino-0.4.3/main/data/shortcuts.ui0000644000000000000000000000617414452563620015416 0ustar rootroot True shortcuts General <ctrl>T Start Conversation <ctrl>G Join Channel Conversation <ctrl>F Search messages <ctrl>U Send a file Navigation <ctrl>Tab Jump to next conversation <ctrl><Shift>Tab Jump to previous conversation dino-0.4.3/main/data/style-dark.css0000644000000000000000000000012114452563620015574 0ustar rootroot.dino-main .overlay-toolbar { background-color: shade(@view_bg_color, 1.5); }dino-0.4.3/main/data/style.css0000644000000000000000000002414614452563620014672 0ustar rootroot/** * This theme file is applied after the operating system theme * It provides sane defaults for things that are very Dino-specific. */ statuspage { opacity: 0.5; } window.dino-main .dino-header-right { background: @theme_base_color; } window.dino-main .dino-header-left { background: @insensitive_bg_color; } window.dino-main headerbar.dino-left label.title { opacity: 0; font-size: 0; color: transparent; } window.dino-main .dino-conversation { background: @theme_base_color; } window.dino-main .dino-conversation undershoot, window.dino-main .dino-conversation viewport /* Some themes set this */ { background: none; } @keyframes highlight { from { background: alpha(@warning_color, 0.5); } to { background: transparent; } } window.dino-main .dino-conversation .highlight-once { animation-duration: 3s; animation-timing-function: ease-out; animation-iteration-count: 1; animation-name: highlight; } window.dino-main .dino-conversation .message-box.highlight { background: @window_bg_color; } window.dino-main .dino-conversation .message-box { padding: 6px 15px 6px 15px; } window.dino-main .dino-conversation .message-box:not(.has-skeleton) { padding-left: 58px; } window.dino-main .unread-count-notify { background-color: alpha(@theme_fg_color, 0.8); color: @theme_base_color; font-family: monospace; border-radius: 999em; padding: 0 .35em; } window.dino-main .unread-count-notify:backdrop { background-color: alpha(@theme_unfocused_fg_color, 0.8); color: @theme_unfocused_base_color; } window.dino-main .unread-count { background-color: alpha(@theme_fg_color, 0.1); color: @theme_fg_color; font-family: monospace; border-radius: 999em; padding: .2em .41em; } window.dino-main .unread-count:backdrop { background-color: alpha(@theme_unfocused_fg_color, 0.1); color: @theme_unfocused_fg_color; } window.dino-main .dino-sidebar > frame { background: @insensitive_bg_color; border-left: 1px solid @borders; border-bottom: 1px solid @borders; } /* Message */ .message-box { transition: background .05s ease; } window.dino-main .dino-conversation .message-box.edit-mode { background: alpha(@theme_selected_bg_color, 0.1); } window.dino-main .dino-conversation .message-box.edit-mode:hover { background: alpha(@theme_selected_bg_color, 0.12); } window.dino-main .dino-conversation .message-box.error { background: alpha(@error_color, 0.1); } window.dino-main .dino-conversation .message-box.error:hover { background: alpha(@error_color, 0.12); } window.dino-main .dino-quote { border-left: 3px solid alpha(@theme_fg_color, 0.2); background: alpha(@theme_fg_color, 0.05); border-color: alpha(@theme_fg_color, 0.2); padding: 10px; } window.dino-main .dino-quote:hover { background: alpha(@theme_fg_color, 0.08); } /* Overlay Toolbar */ .dino-main .overlay-toolbar { padding: 2px; border-radius: 6px; border-spacing: 0; } .dino-main .overlay-toolbar > * { margin-top: 0; margin-bottom: 0; } /* File Widget */ window.dino-main .file-box-outer, window.dino-main .call-box-outer { background: @theme_base_color; border-radius: 3px; border: 1px solid alpha(@theme_fg_color, 0.1); } window.dino-main .file-box, window.dino-main .call-box { margin: 12px 16px 12px 12px; } window.dino-main .file-image-widget { border: 1px solid alpha(@theme_fg_color, 0.1); border-radius: 6px; } window.dino-main .file-image-widget .file-box-outer { color: #eee; background: rgba(0, 0, 0, 0.5); } window.dino-main .file-image-widget .file-box-outer button { color: #eee; background: transparent; border: none; box-shadow: none; } window.dino-main .file-image-widget .file-box-outer button:hover { background: rgba(100, 100, 100, 0.5); } .dino-main .file-image-widget picture { border-radius: 6px; } /* Call widget */ window.dino-main .call-box-outer.incoming { border-color: alpha(@theme_selected_bg_color, 0.3); } window.dino-main .incoming-call-box { background: alpha(@theme_selected_bg_color, 0.1); } window.dino-main .multiparty-participants { border-top: 1px solid alpha(@theme_fg_color, 0.05); background: alpha(@theme_fg_color, 0.04); } /* Reactions */ .dino-main .reaction-grid button { min-height: 16px; min-width: 30px; padding: 4px; } .dino-main .reaction-grid button.own-reaction, .dino-main .reaction-grid .own-reaction button { background-color: alpha(@accent_bg_color, 0.1); border: 1px solid @accent_bg_color; padding: 3px; color: @accent_color; } .dino-main .reaction-grid button.own-reaction:hover, .dino-main .reaction-grid .own-reaction button:hover { background-color: alpha(@accent_bg_color, 0.2); } /* Sidebar */ window.dino-main .dino-sidebar > frame.collapsed { border-bottom: 1px solid @borders; } window.dino-main .dino-sidebar frame.auto-complete { background: @theme_base_color; } window.dino-main .dino-sidebar frame.auto-complete list > row { transition: none; } /* File overlay */ window.dino-main .dino-white-overlay { background: @theme_base_color; } window.dino-main .dino-file-overlay { border-radius: 5px; border: 1px solid alpha(black, 0.2); box-shadow: 0 2px 3px alpha(black, 0.1); } /* Chat Input*/ window.dino-main .dino-chatinput frame box { background: transparent; } window.dino-main .dino-attach-button { min-width: 24px; /* Make button the same width as avatars */ } window.dino-main .dino-attach-button, window.dino-main .dino-chatinput-button button { border: none; background: transparent; box-shadow: none; min-height: 0; padding: 7px 5px; color: alpha(@theme_fg_color, 0.7); outline: none; } window.dino-main .dino-attach-button:hover, window.dino-main .dino-chatinput-button button:hover { color: @theme_selected_bg_color; } window.dino-main .dino-attach-button:backdrop, window.dino-main .dino-chatinput-button button:backdrop { color: alpha(@theme_unfocused_fg_color, 0.6); } window.dino-main .dino-attach-button:active, window.dino-main .dino-attach-button:checked, window.dino-main .dino-chatinput-button button:active, window.dino-main .dino-chatinput-button button:checked { color: alpha(@theme_selected_bg_color, 0.8); } window.dino-main .dino-attach-button:checked:backdrop, window.dino-main .dino-chatinput-button button:checked:backdrop { color: alpha(@theme_unfocused_selected_bg_color, 0.8); } .dino-chatinput, .dino-chatinput textview, .dino-chatinput textview text { background-color: @theme_base_color; } /*Chat input warning*/ box.dino-input-warning frame border { border-color: @warning_color; } box.dino-input-warning frame separator { background-color: @warning_color; border: none; } box.dino-input-warning label { color: mix(@warning_color, @theme_fg_color, 0.5); } /*Chat input error*/ box.dino-input-error frame { border-color: @error_color; } box.dino-input-error frame separator { background-color: @error_color; border: none; } box.dino-input-error .chat-input-status { color: @error_color; } @keyframes input-error-highlight { 0% { transform: translate(0,0); } 25% { transform: translate(-10px,0); } 75% { transform: translate(10px,0); } 100% { transform: translate(0,0); } } box.dino-input-error .chat-input-status.input-status-highlight-once { animation-duration: 0.5s; animation-timing-function: ease-in-out; animation-iteration-count: 1; animation-name: input-error-highlight; } /* Call window */ .dino-call-window decoration { border-radius: 0; } .dino-call-window .titlebar { min-height: 0; } .dino-call-window headerbar, .call-button { box-shadow: none; } .dino-call-window button.call-button { outline: 0; border-radius: 1000px; } .dino-call-window button.white-button { color: #1d1c1d; background: rgba(255,255,255,0.85); border: lightgrey; } .dino-call-window button.white-button:hover { background: rgba(255,255,255,1); } .dino-call-window button.transparent-white-button { color: white; background: rgba(255,255,255,0.15); border: none; } .dino-call-window button.transparent-white-button:hover { background: rgba(255,255,255,0.25); } .dino-call-window menubutton.call-mediadevice-settings-button button.toggle { border-radius: 1000px; min-height: 0; min-width: 0; padding: 3px; margin: 2px; transition-duration: 0; } .dino-call-window menubutton.call-mediadevice-settings-button button.toggle:hover, .dino-call-window menubutton.call-mediadevice-settings-button button.toggle:checked { /* Effect that makes the button slightly larger on hover :) */ border-radius: 1000px; min-height: 0; min-width: 0; padding: 5px; margin: 0; } .dino-call-window .participant-header-bar .titles { background: none; border: none; border-radius: 0; color: #ededec; text-shadow: 0 0 2px black; } .dino-call-window .participant-header-bar { background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0)); border: 0; border-radius: 0; } .dino-call-window .participant-header-bar button:hover:not(.close) { background: rgba(255,255,255,0.15); border-color: rgba(255,255,255,0); box-shadow: none; } .dino-call-window .participant-header-bar button image { color: alpha(white, 0.7); -gtk-icon-shadow: none; } .dino-call-window .participant-header-bar button:hover image { color: white; } .dino-call-window .participant-header-bar button.unencrypted image { color: @error_color; } .dino-call-window .call-bottom-bar { background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.3)); border: 0; } .dino-call-window { background-color: #212121; } .dino-call-window .participant-name { color: white; text-shadow: 1px 1px 2px black; } .dino-call-window .text-no-controls { color: black; background: white; border-radius: 5px; padding: 5px 10px; } .dino-call-window .own-video { box-shadow: 0 0 2px 0 rgba(0,0,0,0.5); } .qrcode-container > contents { background: white; /* Color of the quiet zone. MUST have the same "reflectance" as light modules of the QR code. */ } dino-0.4.3/main/data/unified_main_content.ui0000644000000000000000000001522414452563620017535 0ustar rootroot slide true true vertical False content never 1 placeholder 20 20 20 20 260 You have no open chats Click + to start a chat or join a channel vertical end true true false natural true 600 false content placeholder im.dino.Dino-symbolic True True false 400 400 true dino-0.4.3/main/data/unified_window_placeholder.ui0000644000000000000000000000454114452563620020730 0ustar rootroot dino-0.4.3/main/po/0000755000000000000000000000000014452563620012516 5ustar rootrootdino-0.4.3/main/po/LINGUAS0000644000000000000000000000020214452563620013535 0ustar rootrootar ca cs da de el en eo es eu fa fi fr gl hu id ie is it ja kab ko lb lt lv nb nl oc pl pt pt_BR ro ru sq sv ta tr uk zh_CN zh_TW dino-0.4.3/main/po/ar.po0000644000000000000000000012041014452563620013456 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-12-04 19:47+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "حجم الملف يتجاوز الحد الذي يسمح به الخادم." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "الرسالة طويلة جدًا" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "تم تعديله" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "في انتظار الإرسال…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "فشل التسليم" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "غير مشفّرة" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "تعذر إرسال الرسالة" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "في هذه اللحظة" msgstr[1] "منذ دقيقة واحدة" msgstr[2] "منذ دقيقتين" msgstr[3] "منذ %i دقائق" msgstr[4] "منذ %i دقيقة" msgstr[5] "منذ %i دقيقة" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "الآن" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "أنا" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "أُرسلت الصورة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "أُرسل الملف" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "استلمت الصورة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "اُستلم الملف" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "مكالمة صادرة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "مكالمة واردة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "البارحة" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "ابدأ محادثة" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "المالك" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "المدير" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "عضو" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "مستخدِم" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "ادعو" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "ابدأ محادثة خاصة" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "اطرده" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "منح تصريح الكتابة" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "إبطال تصريح الكتابة" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "دعوة إلى فريق المحادثة" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "اختر ملفا" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "اختر" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "ألغاء" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s مِن %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "هذا المؤتمر لا يسمح لك بإرسال الرسائل." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "طلب الإذن" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "إرسال ملف" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "ابدأ مكالمة" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "مكالمة صوتية" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "مكالمة مرئية" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "ابحث في الرسائل" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "الأعضاء" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "معلومات تصحيح الأخطاء" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "يتصل…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "يرِن…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "الإتصال جارٍ …" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "أنهى %s المكالمة" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "رفض %s المكالمة" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "الكاميرات" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "لا توجد كاميرا." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "الميكروفونات" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "لا يوجد ميكروفون." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "مكبرات الصوت" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "لم يتم العثور على مكبر صوت." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "دعوة إلى المكالمة" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "إبدأ" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "انضم للقناة" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "التالي" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "انضمّ" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "العودة" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "ينضم…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "كلمة السر مطلوبة للدخول إلى غرفة المحادثة" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "محظور مِن الإنضمام أو إنشاء فِرَق محادثة" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "الغرفة غير موجودة" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "لا تملك تصريحًا لإنشاء غرفة" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "غرفة خاصة لذوي العضوية" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "اختر لقبًا آخر" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "الغرفة مكتضة" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "تعذر الاتصال مع %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "العنوان غير صالح" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "اضف" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "ختصارات لوحة المفاتيح" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "عن Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "اليوم" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "لا نتائج" msgstr[1] "نتيجة واحدة" msgstr[2] "نتيجتان" msgstr[3] "%i نتائج" msgstr[4] "%i نتيجة" msgstr[5] "%i نتيجة" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "في %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "مع %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "مكالمة مرئية واردة" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "مكالمة فيديو جماعية واردة" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "مكالمة جماعية واردة" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "رفض" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "قبول" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "طلب اشتراك" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "رفض" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "دعوة إلى %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "دعاك %s إلى %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "طلب إذن" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "طلب %s الاذن بالكتابة في %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "الإعدادات" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "الإعدادات المحلية" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "إرسال إشعارات عند الكتابة" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "إرسال إيصالات القراءة" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "الإشعارات" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "نشط" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "معطّل" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "فقط عندما أُذكر" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "الافتراضي: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "اطلب" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "التصريحات" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "اطلب تصريحًا لكتابة الرسائل" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "تفاصيل فريق المحادثة" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "تفاصيل عن المُتراسل" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "احجبه" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "حجب الإتصال وتحديثات الحالة في كلا الإتجاهين" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "اسم الغرفة" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "وصف الغرفة" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "دائمة" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "سوف تبقى الغرفة حتى و إن غادرها آخِر مقيم" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "يمكن البحث عنها للعلن" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "يمكن للمقيمين تغيير الموضوع" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "السماح بعرض JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "من يمكنه رؤية JIDs الخاصة بالمشارِكين؟" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "كلمة السر" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "كلمة مرور لتقييد الوصول إلى الغرفة" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "تحت الإشراف" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "إلا المشاركون الذين يتمتعون بحق الكتابة يمكنهم ارسال الرسائل" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "الأعضاء فقط" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "يمكن للأعضاء فقط الإلتحاق بالغرفة" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "إدارة التأريخ" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "الحد الأقصى للسجِل المؤرَّخ الصادر عن فريق المحادثة" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "إعدادات الغرفة" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "مرحبا بكم على Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "إتّصل أو قم بإنشاء حساب للمواصلة." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "تهيئة الحساب" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "ليس هناك أية حسابات نشطة" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "إدارة الحسابات" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "كلمة المرور خاطئة" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "شهادة TLS غير صالحة" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "إزالة الحساب %s ؟" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "حذف" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "اختيار الصورة الرمزية" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "الصور" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "كافة الملفات" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "متصل" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "غير متصل" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "خطأ" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "إضافة حساب" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "لم يتمكن الخادم من إثبات أنه %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "شهادة الأمان الخاصة به غير موثوق فيها من قبل نظامك للشغيل." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "أصدرت شهادة الأمان الخاصة به إلى مجال آخر." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "ستصبح شهادة الأمان الخاصة به صالحة فقط في المستقبل." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "انتهت صلاحية شهادة الأمان الخاصة به." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "تسجيل الدخول إلى %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "يمكنك الآن استخدام حساب %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "اسم المستخدم أو كلمة السر غير صحيحة" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "حدث خطأ ما" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "لا ردّ مِن طرف الخادم" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "قم بالتسجيل على %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "يتطلب الخادم التسجيل من خلال موقع ويب" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "افتح موقع الويب" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "إنشاء حساب" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "إطّلع على %s للمزيد من المعلومات حول التسجيل" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "فتح" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "حفظ كـ …" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s و %s و %i آخرون يكتبون…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s و %s و %s يكتبون…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s و %s يكتبان…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s يكتب حاليا…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "بدأتْ المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "بدأتْ منذ %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "ردَدْتَ على هذه المكالمة من جهاز آخر" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "انتهت المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "انتهت على %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "دامتْ %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "مكالمة فائتة" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "فاتتك هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "فاتت %s هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "تم رفض المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "لقد رفضت هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "رفض %s هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "فشلتْ المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" msgstr[2] "" msgstr[3] "" msgstr[4] "" msgstr[5] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "دقيقة" msgstr[2] "دقيقتين" msgstr[3] "بعض دقائق" msgstr[4] "دقائق كثيرة" msgstr[5] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "بضع ثوان" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "يريد هذا المراسل إضافتك إلى قائمة مراسليه" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "جارٍ تنزيل %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "عرض عليك %s: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "الملف المقدّم: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "تم تقديم الملف" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "فشل نقل الملف" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "ملف" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "تحديث الرسالة" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "ليس لديك أية محادثة مفتوحة" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "الحساب" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "الإسم المستعار" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "اللقب" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "إضافة مُراسِل" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "الإشعار عند تلقي رسائل جديدة" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "تحويل الوجوه المبتسمة إلى إيموجي" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "التدقيق الإملائي" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "تطبيق حديث للدردشة عبر XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "إنّ Dino برنامج عصري ومفتوح المصدر للدردشة صُمّم لسطح المكتب. ويُركّز علي تقديم " "تجربة نظيفة وموثوق منها لجابر/XMPP مع أخذ خصوصيتكم بعين الإعتبار." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "وهو يدعم التشفير بواسطة OMEMO و OpenPGP يسمح بإعداد ميزات الخصوصية " "كالإيصالات المقروءة والإخطارات عند الكتابة." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "يقوم Dino بجلب السِجلّ مِن السيرفر ثم يُزامِن الرسائل مع الأجهزة الأخرى." #: main/data/global_search.ui:27 msgid "No active search" msgstr "لا يوجد بحث نشط" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "أكتب للشروع في البحث" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "لا توجد رسائل متطابقة" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "تحقق مِن التدقيق الإملائي أو حاول إزالة فلاتر" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "أرسل" #: main/data/shortcuts.ui:10 msgid "General" msgstr "عام" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "محادثة" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "الإبحار" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "الإنتقال إلى المحادثة التالية" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "الإنتقال إلى المحادثة السابقة" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "الحسابات" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "اللقب المحلي" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "لم يتم إعداد أي حساب بعد" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "إضافة حساب" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "إنشاء حساب" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "إنشاء حساب" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "لا يمكن إنشاء اتصال آمن" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "اتصل" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "إختر خادمًا عموميًا" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "أو قم بإدخال عنوان خادم" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "قم بتسجيل الدخول بدلاً من ذلك" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "إختيار سيرفر آخَر" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "لقد أعددت كل شيء!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "أنهي" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "اضغط هنا لبداية المحادثة أو للإنضمام إلى قناة." #~ msgid "No active conversations" #~ msgstr "ليس هناك أية محادثات نشطة" #~ msgid "Main window with conversations" #~ msgstr "النافذة الرئيسية بالمُحادثات" #~ msgid "%s, %s and %i others" #~ msgstr "%s و %s و %i آخَرين" #~ msgid "You can now start using %s" #~ msgstr "بإمكانك الآن الشروع في استخدام %s" #~ msgid "Open Registration" #~ msgstr "التسجيلات مفتوحة" #~ msgid "Save" #~ msgstr "حفظ" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s و %s" #~ msgid "%s and %s" #~ msgstr "%s و %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "يكتب…" #~ msgstr[1] "يكتب…" #~ msgstr[2] "يكتُبان…" #~ msgstr[3] "يكتبون…" #~ msgstr[4] "يكتبون…" #~ msgstr[5] "يكتبون…" #~ msgid "has stopped typing" #~ msgstr "توقفَ عن الكتابة" #~ msgid "%i search results" #~ msgstr "%i نتيجة بحث" #~ msgid "Discover real JIDs" #~ msgstr "استكشاف مُعرّفي JID الحقيقيين" #~ msgid "Who may discover real JIDs?" #~ msgstr "من يمكنهم استكشاف مُعرّفي JID الحقيقيين؟" #~ msgid "Password required for room entry, if any" #~ msgstr "مطلوب كلمة السر لدخول الغرفة، إن وُجِدت" #~ msgid "Failed connecting to %s" #~ msgstr "فشل الاتصال بـ %s" #~ msgid "Join Conference" #~ msgstr "الإنضمام إلى فريق محادثة" #~ msgid "Communicate happiness." #~ msgstr "أنشر السعادة." #~ msgid "Preferences" #~ msgstr "التفضيلات" #~ msgid "Quit" #~ msgstr "الخروج" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "يجب أن يكون JID على شكل “user@example.com”" #~ msgid "Copy Link Address" #~ msgstr "نسخ العنوان" #~ msgid "Copy" #~ msgstr "نسخ" #~ msgid "Select All" #~ msgstr "اختيار الكل" #~ msgid "Search" #~ msgstr "البحث" #~ msgid "Start Chat" #~ msgstr "إبدأ الدردشة" dino-0.4.3/main/po/ca.po0000644000000000000000000011231514452563620013444 0ustar rootroot# Catalan translation for Dino. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018-2019. # msgid "" msgstr "" "Project-Id-Version: dino 20180510\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-01-09 03:56+0000\n" "Language-Team: Catalan; Valencian \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.10.1\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "El fitxer supera la mida màxima de càrrega del servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "El missatge és massa llarg" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "pendent…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "l'enviament ha fallat" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sense xifrar" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "No s'ha pogut enviar el missatge" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fa %i minut" msgstr[1] "fa %i minuts" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Ara mateix" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Jo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imatge enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fitxer enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imatge rebuda" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fitxer rebut" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Trucada sortint" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Trucada entrant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ahir" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Inicia una conversa" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propietari" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuari" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invita" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Inicia una convesa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsa" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Autoritza el permís d'escriptura" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca el permís d'escriptura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invita a la conferència" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Selecciona el fitxer" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Selecciona" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancel·la" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Aquesta conferència no us permet enviar missatges." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Sol·licita el permís" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Envia un fitxer" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Inicia la trucada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Trucada d'àudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Trucada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Cerca els missatges" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Trucant…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Sonant…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "S'està connectant…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s trucada finalitzada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s ha rebutjat la trucada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Inicia" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Uneix-m'hi al canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Següent" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Uneix-te" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Enrere" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "S'està unint…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Es requereix una contrasenya per entrar a la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Heu estat bandejat d'unir-vos o crear conferències" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala no existeix" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "No esteu autoritzat a crear una sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala només per membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Seleccioneu un sobrenom diferent" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Massa ocupants a la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "No s'ha pogut connectar a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "L'adreça no és vàlida" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Afegeix" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Dreceres de teclat" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Quant al Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Avui" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultat de cerca" msgstr[1] "%i resultats de cerca" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Amb %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Trucada de vídeo entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rebutjar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accepta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Petició de subscripció" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Rebutja" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitació a %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s us ha convidat a %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Sol·licitud de permís" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demana el permís per a escriure a %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Preferències" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Preferències locals" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Envia notificacions de tecleig" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Envia rebuts de lectura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificacions" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Habilitat" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Inhabilitat" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Només quan rebeu una menció" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Per defecte: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Sol·licita" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permisos" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Sol·licita permís per a enviar missatges" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalls de la conferència" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalls del contacte" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloca" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "La comunicació i actualitzacions d'estat en ambdúes direccions estan blocades" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nom de la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descripció de la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistent" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "La sala romandrà després de la sortida del l'últim ocupant" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Cercable públicament" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Els ocupants poden canviar el tema" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permís per a veure JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui té permís per a veure els JID dels membres?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Contrasenya" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Una contrasenya per a restringir l'accés a la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderat" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Només els ocupants amb veu poden enviar missatges" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Només per membres" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Només els membres poden entrar a la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historial dels missatges" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Quantitat màxima de l'historial proporcionat per la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuració de la sala" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Us donem la benvinguda al Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Entreu o creeu un compte per a començar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configureu un compte" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "No hi ha cap compte actiu" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gestiona els comptes" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Contrasenya incorrecta" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "El certificat TLS és invàlid" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Voleu suprimir el compte %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Suprimeix" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Seleccioneu l'àvatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imatges" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Tots els fitxers" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Connectat" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconnectat" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Error" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Afegeix un compte" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "El servidor no ha pogut demostrar que és %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "El seu certificat de seguretat no és de confiança pel seu sistema operatiu." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "El seu certificat de seguretat s'emet a un altre domini." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "El seu certificat de seguretat només serà vàlid en el futur." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "El seu certificat de seguretat ha caducat." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Entra a %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Ara podeu emprar el compte %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Usuari o contrasenya incorrecta" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Ha fallat alguna cosa" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "No s'ha rebut una resposta del servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registre en %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "El servidor requereix un registre a través d'una pàgina web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Obre el lloc web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registre" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Mireu %s per obtenir informació sobre com registrar-vos" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s i %i altres estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s i %s estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s i %s estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s està escrivint…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "S'ha iniciat la trucada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Iniciat fa %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Heu gestionat aquesta trucada en un altre dispositiu" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "La trucada ha finalitzat" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Acabat a %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Durada %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Trucada perduda" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "T'has perdut aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s s'ha perdut aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Trucada rebutjada" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Has rebutjat aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s ha rebutjat aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "La trucada ha fallat" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%d hora" msgstr[1] "%d hores" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%d minut" msgstr[1] "%d minuts" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "uns segons" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Aquest contacte vol afegir-vos a la seva llista de contactes" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "S'està baixant %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ha oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fitxer oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fitxer oferit" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Ha fallat la transferència del fitxer" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fitxer" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualitza el missatge" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "No teniu cap xat obert" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Sobrenom" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Àlias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Afegeix el contacte" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notifica'm quan arriba un missatge nou" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Converteix «smileys» a emoji" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Revisa l'ortografia" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Client de xat XMPP modern" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en " "proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en " "compte la vostra privacitat." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar " "funcionalitats relacionades amb la privacitat com per exemple rebuts de " "lectura i notificacions d'escriptura." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera l'historial del servidor i sincronitza els missatges amb " "altres dispositius." #: main/data/global_search.ui:27 msgid "No active search" msgstr "No hi ha cap cerca activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Teclegeu per a començar una cerca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "No hi ha missatges coincidents" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comproveu l'ortografia o proveu de suprimir filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Envia" #: main/data/shortcuts.ui:10 msgid "General" msgstr "General" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegació" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Salta a la conversa següent" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Salta a la conversa anterior" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Comptes" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Àlias local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "No hi ha cap compte configurat" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Afegeix un compte" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Entra" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crea un compte" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "No s'ha pogut una connexió segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connecta" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Trieu un servidor públic" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "O especifiqueu l'adreça d'un servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Entra" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Trieu un altre servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tot llest!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finalitza" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Feu clic aquí per a començar una conversa o unir-vos a un canal." #~ msgid "No active conversations" #~ msgstr "Cap conversa activa" #~ msgid "Main window with conversations" #~ msgstr "Finestra principal amb converses" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s i %i altres" #~ msgid "You can now start using %s" #~ msgstr "Ara podeu començar a emprar %s" #~ msgid "Open Registration" #~ msgstr "Registre obert" #~ msgid "Save" #~ msgstr "Desa" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s i %s" #~ msgid "%s and %s" #~ msgstr "%s i %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "està escrivint…" #~ msgstr[1] "estan escrivint…" #~ msgid "has stopped typing" #~ msgstr "ha parat d'escriure" #~ msgid "%i search results" #~ msgstr "%i resultats de cerca" #~ msgid "Discover real JIDs" #~ msgstr "Descobreix els JID reals" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui pot descobrir els JID reals?" #~ msgid "Password required for room entry, if any" #~ msgstr "La contrasenya requerida per entrar a la sala, si cal" #~ msgid "Failed connecting to %s" #~ msgstr "No s'ha pogut connectar a %s" #~ msgid "Join Conference" #~ msgstr "Uneix-te a la conferència" #~ msgid "Preferences" #~ msgstr "Preferències" #~ msgid "Quit" #~ msgstr "Surt" #~ msgid "Copy Link Address" #~ msgstr "Copia l'adreça de l'enllaç" #~ msgid "Copy" #~ msgstr "Copia" #~ msgid "Select All" #~ msgstr "Selecciona-ho tot" #~ msgid "Search" #~ msgstr "Cerca" #~ msgid "Send message marker" #~ msgstr "Envia marcador de missatge" #~ msgid "Start Chat" #~ msgstr "Inicia un xat" dino-0.4.3/main/po/cs.po0000644000000000000000000010752214452563620013472 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-01-06 09:53+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.10.1\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Velikost souboru překračuje maximální limit nastavený na serveru." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Zpráva je příliš dlouhá" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "upraveno" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "čeká na vyřízení…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "doručení se nezdařilo" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifrované" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Zprávu nelze odeslat" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Před %i minutou" msgstr[1] "Před %i minutami" msgstr[2] "Před %i minutami" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Nyní" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Já" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Obrázek byl odeslán" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Soubor byl odeslán" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Obrázek byl přijat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Soubor byl přijat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Odchozí hovor" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Příchozí hovor" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Včera" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Začít konverzaci" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Vlastník" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Člen" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Uživatel" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Pozvat" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Začít soukromou konverzaci" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Vyhodit" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Udělit práva pro zapisování" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Odebrat práva pro zapisování" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Pozvat na konferenci" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Vybrat soubor" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Vybrat" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Zrušit" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s z %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Na této konferenci nemůžete posílat zprávy." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Požádat o oprávnění" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Odeslat soubor" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Začít hovor" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hlasový hovor" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Video hovor" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Hledat zprávy" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Členové" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Vyzvánění…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Připojování…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s ukončil hovor" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s odmítl hovor" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Začít" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Připojit se ke kanálu" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Další" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Připojit" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Zpět" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Připojování…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pro vstup do místnosti je zapotřebí heslo" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Zákaz připojování ke konferencím nebo jejich vytváření" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Místnost neexistuje" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Nemáte oprávnění pro vytvoření místnosti" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Místnost pouze pro členy" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Vyberte jinou přezdívku" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "V místnosti je příliš mnoho účastníků" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Připojení k %s se nezdařilo" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Nesprávná adresa" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Přidat" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Klávesové zkratky" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "O Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Dnes" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d. %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i výsledek hledání" msgstr[1] "%i výsledky vyhledávání" msgstr[2] "%i výsledků vyhledávání" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "V %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "S %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Příchozí video hovor" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Odmítnout" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Přijmout" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Požadavek na odběr" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Zamítnout" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Pozvánka do %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s Vás pozval do %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Žádost o oprávnění" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s žádá o oprávnění pro zápis do %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Nastavení" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokální Nastavení" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Odesílat notifikace o psaní" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Odesiílat potvrzení o přečtení" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notifikace" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Zapnuto" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Vypnuto" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Pouze pokud je zmíněno" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Výchozí: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Žádost" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Oprávnění" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Požádat o oprávnění k odesílání zpráv" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detaily Konference" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detaily Kontaktu" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Zablokovat" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Komunikace a aktualizace stavu jsou blokovány v obou směrech" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Název místnosti" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Popis místnosti" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Stálá" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Místnost bude zachována i poté, co ji opustí poslední účastník" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Veřejně vyhledatelná" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Účastníci mohou měnit předmět" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Oprávnění k náhledu JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kdo může vidět JID účastníků?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Heslo" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Heslo pro omezení přístupu do místnosti" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderováno" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Pouze účastníci s hlasem mohou zasílat zprávy" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Pouze pro členy" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Do místnosti mohou vstoupit pouze členové" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historie zpráv" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maximální výše nevyřízených žádostí vydaných místností" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Nastavení místnosti" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Vítejte v Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Chcete-li začít, přihlaste se nebo si vytvořte účet." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Nastavit účet" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Žádné aktivní účty" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Správa účtů" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Nesprávné heslo" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Neplatný TLS certifikát" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Odstranit účet %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Odstranit" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Vybrat avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Obrázky" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Všechny soubory" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Připojeno" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Odpojeno" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Chyba" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Přidat účet" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Server nemůže dokázat, že je %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Operační systém nedůvěřuje jeho bezpečnostnímu certifikátu." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Jeho bezpečnostní certifikát byl vydán pro jinou doménu." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Jeho bezpečnostní certifikát bude platný až v budoucnu." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Platnost jeho bezpečnostního certifikátu vypršela." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Přihlásit se k %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Účet %s můžete nyní používat." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nesprávné uživatelské jméno nebo heslo" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Něco se pokazilo" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Žádná odpověď od serveru" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrovat na %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Server vyžaduje přihlášení prostřednictvím webové stránky" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Otevřít internetovou stránku" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrovat" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Podívejte se na %s pro získání informací ohledně způsobu přihlášení" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s a %i dalších píše …" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s a %s se píší…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s a %s píší…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s píše…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Hovor začal" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Začal před %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Tento hovor byl přijat na jiném zařízení" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Hovor byl ukončen" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Skončil v %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Trval %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Nepřijatý hovor" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Máte zmeškaný hovor" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s zmeškal(a) tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Hovor byl odmítnut" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Odmítl(a) jste tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s odmítl(a) tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Hovor selhal" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hodina" msgstr[1] "%i hodiny" msgstr[2] "%i hodin" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Tento kontakt by si vás rád přidal do svého seznamu kontaktů" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Stahování %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ke stažení: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Soubor ke stažení: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Soubor ke stažení" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Přenos souboru se nezdařil" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aktualizovat zprávu" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nemáte žádné otevřené chaty" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Účet" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Přezdívka" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Přidat kontakt" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Upozornit, jakmile přijde nová zpráva" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Převádět smajlíky na emotikony" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Kontrola pravopisu" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Moderní XMPP klient" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem " "je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na " "zachování vašeho soukromí." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje " "konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení " "a oznámení o psaní." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino načítá historii ze serveru a synchronizuje zprávy s ostatními " "zařízeními." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Žádné aktivní vyhledávání" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Pro zahájení vyhledávání začněte psát" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Žádné odpovídající zprávy" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Zkontrolujte pravopis nebo zkuste odstranit filtry" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Odeslat" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Obecné" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Konverzace" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigace" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Přejít na další konverzaci" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Přejít na předchozí konverzaci" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Účty" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Místní alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nejsou nakonfigurovány žádné účty" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Přidat účet" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Přihlásit se" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Vytvořit účet" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Nepodařilo se navázat zabezpečené připojení" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Připojit" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Vyberte veřejný server" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Nebo zadejte adresu serveru" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Místo toho se přihlásit" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Vybrat jiný server" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Vše připraveno!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Dokončit" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Kliknutím sem zahájíte konverzaci nebo se připojíte ke kanálu." dino-0.4.3/main/po/da.po0000644000000000000000000007542314452563620013455 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-09-14 14:36+0000\n" "Language-Team: none\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.9-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Filen overstiger serverens maksimale uploadstørrelse." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Mig" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Billede sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Billede modtaget" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil modtaget" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/de.po0000644000000000000000000011451614452563620013456 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Die Datei übersteigt die maximale Uploadgröße des Servers." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Nachricht zu lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "bearbeitet" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "ausstehend…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "Zustellung fehlgeschlagen" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Unverschlüsselt" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Nachricht kann nicht gesendet werden" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "vor %i min" msgstr[1] "vor %i min" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Gerade eben" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ich" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild gesendet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Datei gesendet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild empfangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Datei empfangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Ausgehender Anruf" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Eingehender Anruf" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gestern" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Unterhaltung starten" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigentümer" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Mitglied" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Gast" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Einladen" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Private Unterhaltung beginnen" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Hinauswerfen" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Schreibberechtigung erteilen" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Schreibberechtigung entziehen" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Zur Konferenz einladen" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Datei auswählen" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Auswählen" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Abbrechen" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s aus %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Diese Konferenz erlaubt es dir nicht, Nachrichten zu senden." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Berechtigung anfordern" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Datei senden" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Anruf starten" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audioanruf" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videoanruf" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Nachrichten suchen" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Mitglieder" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Debug-Information" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Anrufen…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Klingeln…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Verbinden…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s hat den Anruf beendet" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s hat den Anruf abgelehnt" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Keine Kamera gefunden." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofone" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Kein Mikrofon gefunden." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Lautsprecher" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Kein Lautsprecher gefunden." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Zum Anruf einladen" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Starten" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Kanal beitreten" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Weiter" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Beitreten" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Zurück" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Beitreten…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Raum erfordert Passwort" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Beitreten oder erstellen der Konferenz verboten" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Raum existiert nicht" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Raum erzeugen verboten" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Raum nur für Mitglieder" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wähle einen anderen Spitznamen" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Zu viele Nutzer im Raum" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Verbindung zu %s konnte nicht hergestellt werden" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ungültige Adresse" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Hinzufügen" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Tastenkombinationen" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Info zu Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Heute" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i Suchergebnis" msgstr[1] "%i Suchergebnisse" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Mit %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Eingehender Videoanruf" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Eingehender Gruppen-Videoanruf" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Eingehender Gruppenanruf" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Ablehnen" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Annehmen" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Kontaktanfrage" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Ablehnen" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Einladung zu %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s hat dich in %s eingeladen" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Berechtigungsanfrage" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s erbittet die Berechtigung zum Schreiben in %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Einstellungen" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokale Einstellungen" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Tippbenachrichtigungen senden" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Lesebestätigungen senden" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Benachrichtigungen" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Unterhaltung anheften" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Heftet die Unterhaltung an den Anfang der Unterhaltungsliste" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "An" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Aus" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Nur bei Erwähnung" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Anfragen" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Berechtigungen" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Berechtigung zum Senden von Nachrichten anfordern" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferenzdetails" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetails" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blockieren" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Kommunikation und Statusaktualisierungen sind in beide Richtungen blockiert" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Name des Raums" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Beschreibung des Raums" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Dauerhaft" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" "Der Raum bleibt erhalten, nachdem der letzte Benutzer ihn verlassen hat" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Öffentlich sichtbar" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Nutzer können das Thema ändern" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Erlaubnis JIDs zu sehen" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wer darf die JIDs von Nutzern sehen?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Passwort" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Ein Passwort, um den Zugang zum Raum zu beschränken" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderiert" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Nur Nutzer mit Stimme können Nachrichten senden" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Ausschließlich Mitglieder" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Nur Mitglieder dürfen den Raum betreten" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Gesprächsverlauf" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Anzahl der für diesen Raum gespeicherten Nachrichten" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Raum Konfiguration" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Willkommen bei Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Melde dich an oder erstelle ein Konto, um loszulegen." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Konto einrichten" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Keine Konten aktiv" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Konten verwalten" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Passwort falsch" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Ungültiges TLS-Zertifikat" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Konto %s löschen?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Löschen" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Profilbild auswählen" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Bilder" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Alle Dateien" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Verbunden" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Nicht verbunden" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Fehler" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Konto hinzufügen" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Der Server konnte nicht beweisen, dass er %s ist." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Sein Sicherheitszertifikat wird von deinem Betriebssystem nicht akzeptiert." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Sein Sicherheitszertifikat ist für eine andere Domain ausgestellt." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Sein Sicherheitszertifikat ist erst in der Zukunft gültig." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Sein Sicherheitszertifikat ist abgelaufen." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Bei %s einloggen" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Du kannst das Konto %s jetzt benutzen." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Benutzername oder Passwort falsch" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Etwas ist schief gelaufen" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Keine Antwort vom Server" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Konto auf %s erstellen" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Der Server erfordert die Registrierung durch eine Webseite" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Webseite öffnen" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrieren" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Eventuell sind auf %s Informationen zur Registrierung zu finden" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Nachricht bearbeiten" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Du" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Reaktion hinzufügen" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Öffnen" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Speichern unter…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s und %i andere tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s und %s tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s und %s tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s tippt…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Anruf gestartet" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Gestartet vor %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Du hast diesen Anruf mit einem anderen Gerät geführt" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Anruf beendet" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Endete um %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Dauerte %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Du hast diesen Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s hat diesen Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Du hast diesen Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s hat diesen Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Fehlgeschlagener Anruf" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i Stunde" msgstr[1] "%i Stunden" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i Minute" msgstr[1] "%i Minuten" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "ein paar Sekunden" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Dieser Kontakt will dich zu seiner Kontaktliste hinzufügen" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Antworten" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Lade %s herunter…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s angeboten: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Datei angeboten: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Datei angeboten" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Dateiübertragung fehlgeschlagen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Datei" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Nachricht aktualisieren" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du hast keine offenen Chats" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klicke auf +, um einen Chat zu starten oder einem Kanal beizutreten" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Anzeigename" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontakt hinzufügen" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Benachrichtigen bei Empfang einer neuen Nachricht" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Smileys zu Emojis umwandeln" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Rechtschreibung prüfen" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Modernes XMPP-Chat-Programm" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet " "ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen " "Schwerpunkt auf Privatsphäre." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und " "enthält Privatsphäre-Einstellungen zu Lesebestätigungen und " "Tippbenachrichtigungen." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit " "anderen Geräten." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Keine Suche gestartet" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tippe um eine Suche zu beginnen" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Keine Nachrichten gefunden" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Überprüfe die Schreibweise oder entferne Filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Senden" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Allgemein" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Unterhaltung" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigation" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Zur nächsten Unterhaltung springen" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Zur letzten Unterhaltung springen" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Konten" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokaler Alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Keine Konten konfiguriert" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Konto hinzufügen" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Einloggen" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Konto erstellen" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Es konnte keine sichere Verbindung hergestellt werden" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Verbinden" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Wähle einen öffentlichen Server" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Oder gib eine Serveradresse an" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Stattdessen einloggen" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Anderen Server wählen" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Fertig!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Fertig" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klicke hier, um eine Unterhaltung zu starten oder einem Kanal beizutreten." #~ msgid "Video call incoming" #~ msgstr "Eingehender Videoanruf" #~ msgid "Call incoming" #~ msgstr "Eingehender Anruf" #~ msgid "Establishing call" #~ msgstr "Anruf aufbauen" #~ msgid "Video call establishing" #~ msgstr "Videoanruf aufbauen" #~ msgid "Call establishing" #~ msgstr "Rufaufbau" #~ msgid "Call in progress…" #~ msgstr "Anruf wird ausgeführt…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Dauerte %s" #~ msgid "seconds" #~ msgstr "Sekunden" #~ msgid "No active conversations" #~ msgstr "Keine Unterhaltung aktiv" #~ msgid "Main window with conversations" #~ msgstr "Hauptfenster mit Konversationen" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s und %i weitere" #~ msgid "You can now start using %s" #~ msgstr "Du kannst %s ab jetzt nutzen" #~ msgid "Open Registration" #~ msgstr "Registrierung öffnen" #~ msgid "Save" #~ msgstr "Speichern" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s und %s" #~ msgid "%s and %s" #~ msgstr "%s und %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippt gerade…" #~ msgstr[1] "tippen gerade…" #~ msgid "has stopped typing" #~ msgstr "hat aufgehört zu tippen" #~ msgid "%i search results" #~ msgstr "%i Suchergebnisse" #~ msgid "Discover real JIDs" #~ msgstr "Echte JIDs finden" #~ msgid "Who may discover real JIDs?" #~ msgstr "Wer kann echte JIDs sehen?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passwort zum Betreten des Raums, falls gesetzt" #~ msgid "Failed connecting to %s" #~ msgstr "Verbindung zu %s fehlgeschlagen" #~ msgid "Join Conference" #~ msgstr "Konferenz beitreten" #~ msgid "Communicate happiness." #~ msgstr "Glücklich(keit) kommunizieren." #~ msgid "Preferences" #~ msgstr "Einstellungen" #~ msgid "Quit" #~ msgstr "Beenden" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID sollte das Format “benutzer@beispiel.de” haben" #~ msgid "Copy Link Address" #~ msgstr "Link-Adresse Kopieren" #~ msgid "Copy" #~ msgstr "Kopieren" #~ msgid "Select All" #~ msgstr "Alles Auswählen" #~ msgid "Search" #~ msgstr "Suchen" #~ msgid "Send message marker" #~ msgstr "Lesebestätigung senden" #~ msgid "Start Chat" #~ msgstr "Chat starten" #~ msgid "Request presence updates" #~ msgstr "Online-Status erfragen" #~ msgid "Join on startup" #~ msgstr "Beim Start beitreten" #~ msgid "Add Chat" #~ msgstr "Chat hinzufügen" #~ msgid "Name" #~ msgstr "Name" #~ msgid "Description" #~ msgstr "Beschreibung" dino-0.4.3/main/po/dino.pot0000644000000000000000000007532714452563620014211 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/el.po0000644000000000000000000012142014452563620013456 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Το αρχείο υπερβαίνει το μέγιστο μέγεθος μεταφόρτωσης του διακομιστή." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Το μήνυμα είναι πολύ μεγάλο" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "διόρθωση" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "εκκρεμεί…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "αποτυχία παράδοσης" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Μη κρυπτογραφημένο" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Δεν είναι δυνατή η αποστολή μηνύματος" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i λεπτό πριν" msgstr[1] "%i λεπτά πριν" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Μόλις τώρα" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Εγώ" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Η εικόνα εστάλη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Το αρχείο εστάλη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Η εικόνα ελήφθη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Το αρχείο ελήφθη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Εξερχόμενη κλήση" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Εισερχόμενη κλήση" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Εχθές" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Έναρξη Συνομιλίας" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Ιδιοκτήτης" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Διαχειριστής" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Μέλος" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Χρήστης" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Πρόσκληση" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Ξεκινήστε ιδιωτική συνομιλία" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Αποβολή" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Εκχώρηση άδειας εγγραφής" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Ανάκληση άδειας εγγραφής" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Πρόσκληση σε Συνεδρία" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Επιλέξτε αρχείο" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Επιλογή" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Ακύρωση" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s από %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Αυτή η συνεδρία δεν σας επιτρέπει να στέλνετε μηνύματα." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Ζητήστε άδεια" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Αποστολή αρχείου" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Έναρξη κλήσης" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Ηχητική κλήση" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Βίντεο κλήση" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Αναζήτηση μηνυμάτων" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Μέλη" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Πληροφορίες εντοπισμού σφαλμάτων" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Κλήση…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Κουδούνισμα…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Σύνδεση…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s τερμάτισε την κλήση" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s απέρριψε την κλήση" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Κάμερες" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Δεν βρέθηκε κάμερα." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Μικρόφωνα" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Δε βρέθηκε μικρόφωνο." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Ηχεία" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Δε βρέθηκε ηχείο." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Πρόσκληση σε Κλήση" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Έναρξη" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Συμμετοχή σε Ομαδική συνομιλία" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Επόμενο" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Συμμετοχή" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Πίσω" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Είσοδος…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Απαιτείται κωδικός πρόσβασης για την είσοδο στο δωμάτιο συνομιλίας" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Απαγορεύεται η συμμετοχή ή η δημιουργία συνεδρίου" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Το δωμάτιο συζήτησης δεν υπάρχει" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Δεν επιτρέπεται η δημιουργία δωματίου συνομιλίας" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Είσοδος μόνο για Μέλη" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Επιλέξτε ένα διαφορετικό ψευδώνυμο" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Πάρα πολλοί χρήστες στο δωμάτιο συνομιλίας" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Δεν ήταν δυνατή η σύνδεση στο %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Μη έγκυρη διεύθυνση" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Προσθήκη" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Συντομεύσεις πληκτρολογίου" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Σχετικά με το Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Σήμερα" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i αποτέλεσμα αναζήτησης" msgstr[1] "%i αποτελέσματα αναζήτησης" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Στο %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Με %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Εισερχόμενη βιντεοκλήση" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Εισερχόμενη ομαδική βιντεοκλήση" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Εισερχόμενη ομαδική κλήση" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Απόρριψη" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Αποδοχή" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Αίτημα συνδρομής" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Απόρριψη" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Πρόσκληση σε %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s σας προσκάλεσε στο %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Αίτημα άδειας" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ζητά την άδεια να γράψει σε %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Ρυθμίσεις" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Τοπικές Ρυθμίσεις" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Αποστολή ειδοποιήσεων πληκτρολόγησης" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Αποστολή αποδείξεων ανάγνωσης" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Ειδοποιήσεις" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Ενεργό" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Ανενεργό" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Μόνο όταν υπάρξει αναφορά σε εμένα" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Προεπιλογή: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Αίτηση" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Άδειες" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Ζητήστε άδεια για αποστολή μηνυμάτων" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Λεπτομέρειες Συνεδρίου" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Στοιχεία Επικοινωνίας" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Αποκλεισμός" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Η επικοινωνία και οι ενημερώσεις κατάστασης προς οποιαδήποτε κατεύθυνση " "είναι αποκλεισμένες" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Όνομα του δωματίου συνομιλίας" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Περιγραφή του δωματίου συνομιλίας" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Διηνεκές" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Το δωμάτιο θα παραμείνει μετά την αποχώρηση του τελευταίου χρήστη" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Δημόσια αναζητήσιμο" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Οι χρήστες έχουν τη δυνατότητα να αλλάξουν θέμα" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Άδεια θέασης JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Ποιος επιτρέπεται να δει τα JIDs των χρηστών;" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Κωδικός πρόσβασης" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" "Ένας κωδικός πρόσβασης για τον περιορισμό της πρόσβασης στο δωμάτιο " "συνομιλίας" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Εποπτευόμενο" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Μόνο οι χρήστες με \"φωνή\" μπορούν να στέλνουν μηνύματα" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Μόνο για μέλη" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Μόνο μέλη μπορούν να εισέλθουν στην δωμάτιο συνομιλίας" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Ιστορικό μηνυμάτων" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Μέγιστο ιστορικό που εκδίδεται από το δωμάτιο συνομιλίας" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Διαμόρφωση δωματίου συνομιλίας" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Καλώς ήρθατε στο Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Συνδεθείτε ή δημιουργήστε έναν λογαριασμό για να ξεκινήσετε." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Ρύθμιση λογαριασμού" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Δεν υπάρχουν ενεργοί λογαριασμοί" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Διαχείριση λογαριασμών" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Λάθος κωδικός" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Μη έγκυρο πιστοποιητικό TLS" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Αφαίρεση λογαριασμού %s;" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Αφαίρεση" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Επιλογή avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Εικόνες" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Όλα τα αρχεία" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Συνδεδεμένο" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Ασύνδετο" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Λάθος" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Προσθήκη Λογαριασμού" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Το πιστοποιητικό ασφαλείας του δεν είναι αξιόπιστο από το λειτουργικό σας " "σύστημα." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Το πιστοποιητικό ασφαλείας του έχει εκδοθεί για διαφορετικό domain." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Το πιστοποιητικό ασφαλείας του θα είναι έγκυρο μόνο στο μέλλον." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Το πιστοποιητικό ασφαλείας του έχει λήξει." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Συνδεθείτε στο %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Τώρα μπορείτε να χρησιμοποιήσετε τον λογαριασμό %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Λάθος όνομα χρήστη ή κωδικός" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Κάτι πήγε στραβά" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Καμία απάντηση από τον διακομιστή" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Εγγραφή στο %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Ο διακομιστής απαιτεί την εγγραφή μέσω ενός ιστότοπου" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Άνοιγμα ιστότοπου" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Εγγραφή" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ελέγξτε %s για πληροφορίες σχετικά με τον τρόπο εγγραφής" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Άνοιγμα" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Αποθήκευση ως…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s και %i άλλοι πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s και %s πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s και %s πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s πληκτρολογεί…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Η κλήση ξεκίνησε" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Ξεκίνησε πριν από %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Διαχειριστήκατε αυτήν την κλήση σε άλλη συσκευή" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Τέλος κλήσης" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Τερματισμός στις %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Διήρκεσε %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Αναπάντητη κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Χάσατε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s έχασε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Η κλήση απορρίφθηκε" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Απορρίψατε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s απέρριψε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Αποτυχία κλήσης" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ώρα" msgstr[1] "%i ώρες" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i λεπτό" msgstr[1] "%i λεπτά" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "μερικά δευτερόλεπτα" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Αυτή η επαφή θα ήθελε να σας προσθέσει στη λίστα επαφών της" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Λήψη %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s για μεταφόρτωση: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Αρχείο για μεταφόρτωση: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Αρχείο για μεταφόρτωση" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Η μεταφορά αρχείων απέτυχε" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Ενημέρωση μηνύματος" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Δεν έχετε ανοιχτές συνομιλίες" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Λογαριασμός" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Υποκοριστικό (nickname)" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Ψευδώνυμο" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Προσθήκη Επαφής" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Ειδοποίηση όταν έρχεται νέο μήνυμα" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Μετατροπή smileys σε emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Ορθογραφικός έλεγχος" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Σύγχρονος XMPP Chat Client" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Το Dino είναι ένας σύγχρονος πελάτης συνομιλίας ανοιχτού κώδικα για desktop " "υπολογιστές. Επικεντρώνεται στην παροχή μιας καθαρής και αξιόπιστης " "εμπειρίας Jabber/XMPP έχοντας παράλληλα υπόψη την προστασία των προσωπικών " "δεδομένων σας." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Υποστηρίζει κρυπτογράφηση από άκρο σε άκρο με OMEMO και OpenPGP και " "επιτρέπει την ρύθμιση λειτουργιών που σχετίζονται με το απόρρητο, όπως " "αποδείξεις ανάγνωσης και ειδοποιήσεις πληκτρολόγησης." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Το Dino ανακτά το ιστορικό από τον διακομιστή και συγχρονίζει τα μηνύματα με " "άλλες συσκευές." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Δεν υπάρχει ενεργή αναζήτηση" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Πληκτρολογήστε για να ξεκινήσει μια αναζήτηση" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Δεν βρέθηκαν ανάλογα μηνύματα" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ελέγξτε την ορθογραφία ή προσπαθήστε να αφαιρέσετε φίλτρα" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Αποστολή" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Γενικά" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Συνομιλία" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Πλοήγηση" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Μετάβαση στην επόμενη συνομιλία" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Μετάβαση στην προηγούμενη συνομιλία" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Λογαριασμοί" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Τοπικό ψευδώνυμο" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Δεν έχουν διαμορφωθεί λογαριασμοί" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Προσθήκη λογαριασμού" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Σύνδεση" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Δημιουργία λογαριασμού" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Δεν ήταν δυνατή η δημιουργία ασφαλούς σύνδεσης" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Σύνδεση" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Επιλέξτε έναν δημόσιο διακομιστή" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ή καθορίστε μια διεύθυνση διακομιστή" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Αντ' αυτού, συνδεθείτε" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Επιλέξτε άλλο διακομιστή" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Όλα έτοιμα!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Ολοκλήρωση" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Κάντε κλικ εδώ για να ξεκινήσετε μια συνομιλία ή να εισέλθετε σε ένα " #~ "δωμάτιο ομαδικής συνομιλίας." dino-0.4.3/main/po/en.po0000644000000000000000000007451714452563620013476 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/eo.po0000644000000000000000000011120714452563620013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-07-10 10:14+0000\n" "Language-Team: Esperanto \n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.13.1-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "La dosiero superas la maksimuman grandon de alŝuto al la servilo." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mesaĝo tro longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "redaktita" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "sendata…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "sendo malsukcesis" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Neĉifrita" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Ne povas sendi mesaĝon" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d-a %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d-a %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Antaŭ %i minuto" msgstr[1] "Antaŭ %i minutoj" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Ĵus" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Mi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bildon sendis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Dosieron sendis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bildon ricevis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Dosieron ricevis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Eliranta alvoko" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Envenanta alvoko" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Hieraŭ" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Komenci Konversacion" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Posedanto" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administranto" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Uzanto" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviti" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Komenci privatan interparolon" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Forpeli" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Doni skriban permeson" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Eksvalidigi skriban permeson" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviti en la Konferencon" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Elekti dosieron" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Elekti" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Nuligi" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s el %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ĉi tiu konferenco ne permesas al vi sendi mesaĝojn." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Peti permeson" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Sendi dosieron" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Komenci alvokon" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Telefona alvoko" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videa alvoko" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Serĉi mesaĝojn" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anoj" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informoj por sencimigo" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Alvokante…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Voksonante…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Konektado…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s finis la alvokon" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s malakceptis la alvokon" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Fotiloj" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kamerao ne troviĝis." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonoj" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofono ne troviĝis." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Laŭtparoliloj" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Laŭtparolilo ne troviĝis." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviti al alvoko" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Komenci" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Aliĝi al Kanalo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Sekva" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Aliĝi" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Reen" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Aliĝante…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pasvorto estas bezonata por eniri en babilejon" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Forbarita je aliĝi aŭ krei konferencon" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Babilejo ne ekzistas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ne estas permesata, ke vi kreas babilejon" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Nurmembra babilejo" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Elektu malsaman kromnomon" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tro da ĉeestantoj en babilejo" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Ne povus konekti al %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Nevalida adreso" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Aldoni" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Fulmoklavoj" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Pri Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hodiaŭ" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d-a %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i serĉrezulto" msgstr[1] "%i serĉrezultoj" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Kun %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Envenanta videa alvoko" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Envenanta videa grupa alvoko" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Envenanta grupa alvoko" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rifuzi" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akcepti" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Abona peto" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Rifuzi" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "invitilo por %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "invitilo de %s por %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Peto pri permeso" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s petas la permeson skribi en %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Agordoj" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokaj Agordoj" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Sciigi pri tajpado" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Sendi kvitancojn de legiteco" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Sciigoj" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Ek" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "For" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Nur menciite" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Defaŭlto: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Peto" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permesoj" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Peti permeson por sendi mesaĝojn" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detaloj pri Konferenco" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktaj Detaloj" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Forbari" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Komunikado kaj statoĝisdatigoj ambaŭdirektaj estas forbaritaj" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nomo de la babilejo" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Priskribo de la babilejo" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Daŭra" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "La babilejo daŭros post eliro de la lasta ĉeestinto" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Publike serĉebla" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Ĉeestantoj rajtas ŝanĝi temon" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permeso por vidi JID-ojn" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kiu rajtas vidi la JID-ojn de la ĉeestantoj?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Pasvorto" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Pasvorto por limigi aliron al la babilejo" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Kontrolata" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Nur ĉeestantoj kun voĉo rajtas sendi mesaĝojn" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Nur anoj" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Nur anoj rajtas eniri la babilejon" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Mesaĝa historio" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maksimuma nombro da historiaj mesaĝoj donotaj de la babilejo" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Babilejaj Agordoj" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Bonvenon al Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Ensalutu aŭ kreu konton por komenci." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Agordi konton" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Neniuj aktivaj kontoj" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Administri kontojn" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Malĝusta pasvorto" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Nevalida TLSa atesto" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Ĉu forigi konton %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Forigi" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Elekti profilbildon" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Bildoj" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Ĉiuj dosieroj" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Konektita" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Malkonektita" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Eraro" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Aldoni Konton" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "La servilo ne povis pruvi, ke ĝi estas %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Ĝia atestilo ne estas fidata de via mastruma sistemo." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Ĝia atestilo estas eldonita por alia retejo." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Ĝia atestilo validos nur estonte." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Ĝia atestilo eksvalidiĝis." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Ensalutu al %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Vi povas nun uzi la konton %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Malĝusta uzantnomo aŭ pasvorto" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Io misis" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Nenia respondo de servilo" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registriĝi sur %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "La servilo postulas registradon per retejo" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Malfermi retejon" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registriĝi" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Rigardi %s por informo pri kiel registriĝi" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Malfermi" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Konservi kiel…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s, kaj %i ceteruloj tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, kaj %s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s kaj %s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Alvoko komenciĝis" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Komencita antaŭ %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Vi akceptis ĉi tiun alvokon per alia aparato" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Alvoko finiĝis" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Finiĝis je %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Daŭris %sn" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Alvoko ne estis akceptita" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Vi ne akceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s ne akceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Alvoko estis malakceptita" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Vi malakceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s malakceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Alvoko malsukcesis" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i horo" msgstr[1] "%i horoj" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutoj" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "kelke da sekundoj" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Ĉi tiu kontakto ŝatus aligi vin al sia kontaktlisto" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Elŝutado de %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofertis: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Dosiero ofertita: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Dosiero ofertita" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Dosiertransigo malsukcesis" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Ĝisdatigi mesaĝon" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vi ne havas malfermitajn babilojn" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Kaŝnomo" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Kaŝnomo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Aldoni kontakton" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Sciigi pri envenaj mesaĝoj" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Grafikigi tekstajn mienetojn" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Kontroli ortografion" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Moderna XMPP-Retebabililo" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas " "provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian " "privatecon." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi " "funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj " "aparatoj." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Neniu aktiva serĉo" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Ektajpu por ekserĉi" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Neniuj kongruaj mesaĝoj" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Korektu la ortografion aŭ provu forigi filtrilojn" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Sendi" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Ĝenerala" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Konversacio" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigado" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Salti al sekva konversacio" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Salti al antaŭa konversacio" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Kontoj" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Loka kaŝnomo" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Neniu aktiva konto" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Aldoni konton" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Ensaluti" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Krei konton" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Ne povis sekure konekti" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Konekti" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Elektu publikan servilon" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Aŭ specifu servilan adreson" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Ensaluti anstataŭe" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Elekti alian servilon" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Ĉio pretas!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Fini" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Alklaku ĉi tie por komenci konversacion aŭ aliĝi al kanalo." #~ msgid "No active conversations" #~ msgstr "Neniu aktiva interparolo" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s, kaj %i aliaj" #~ msgid "You can now start using %s" #~ msgstr "Vi nun povas komenci uzanta %s" #~ msgid "Open Registration" #~ msgstr "Malfermi registriĝon" #~ msgid "Save" #~ msgstr "Konservi" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s kaj %s" #~ msgid "%s and %s" #~ msgstr "%s kaj %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tajpas…" #~ msgstr[1] "tajpas…" #~ msgid "has stopped typing" #~ msgstr "ĉesis tajpi" #~ msgid "Discover real JIDs" #~ msgstr "Eltrovi verajn JID-ojn" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kiu rajtas eltrovi verajn JID-ojn?" #~ msgid "Failed connecting to %s" #~ msgstr "Malsukcesis konektanta al %s" #~ msgid "Join Conference" #~ msgstr "Aliĝi al Kunveno" #~ msgid "Preferences" #~ msgstr "Preferoj" #~ msgid "Quit" #~ msgstr "Ĉesi" #~ msgid "Search" #~ msgstr "Serĉi" #~ msgid "Send message marker" #~ msgstr "Sendi mesaĝmarkilon" #~ msgid "Start Chat" #~ msgstr "Ekbabili" #~ msgid "Request presence updates" #~ msgstr "Peti sciigojn pri ĉeesto" #~ msgid "Join on startup" #~ msgstr "Aliĝi je lanĉo" #~ msgid "Add Chat" #~ msgstr "Aldoni Babilon" dino-0.4.3/main/po/es.po0000644000000000000000000011500114452563620013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-02-01 20:52+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "El archivo excede el tamaño máximo de subida del servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mensaje demasiado largo" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "pendiente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "entrega fallida" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sin cifrado" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "No se pudo enviar el mensaje" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "hace %i min" msgstr[1] "hace %i mins" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Justo ahora" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Yo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagen enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Archivo enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagen recibida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Archivo recibido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Llamada saliente" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Llamada entrante" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ayer" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Iniciar Conversación" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propietario" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Miembro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuario" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invitar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversación privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permiso para escribir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar permiso para escribir" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitar a Conversación en grupo" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Seleccionar archivo" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Seleccionar" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s desde %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Esta conversación en grupo no permite enviar mensajes." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicitar permiso" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Enviar un archivo" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar llamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audiollamada" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videollamada" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Buscar mensajes" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Miembros" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Información de depuración" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Llamando…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Sonando…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conectando…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s finalizó la llamada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s ha rechazado la llamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Cámaras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "No se encontró una cámara." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Micrófonos" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "No se encontró un micrófono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altavoces" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "No se encontraron altavoces." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invitar a la Llamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Unirse a Conversación en grupo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Siguiente" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Unirse" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Volver" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Uniéndose…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Contraseña requerida para entrar a la conversación" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Prohibido unirse o crear una conversación en grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala no existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "No está permitido crear una conversación en grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "La conversación es solo para miembros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Elige un alias diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "La conversación tiene demasiados ocupantes" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "No se pudo conectar a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Dirección inválida" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Añadir" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Acerca de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoy" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado de búsqueda" msgstr[1] "%i resultado de búsquedas" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Videollamada entrante" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Llamada entrante de video en grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Llamada grupal entrante" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rechazar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Solicitud de suscripción" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Denegar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitación a %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s te ha invitado a %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Solicitud de permiso" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s solicita permiso para escribir en %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Configuraciones" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Configuraciones locales" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Enviar notificación de escritura" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Enviar notificación de lectura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificaciones" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "fijar la conversación" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" "Fija la conversación en la parte superior de la lista de conversaciones" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Sí" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "No" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Solo cuando te mencionan" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Por defecto: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Solicitar" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permisos" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Solicitar permiso para enviar mensajes" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalles de Conversación en grupo" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalles de Contacto" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloquear" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "La comunicación y las actualizaciones de estado en cualquier dirección están " "bloqueadas" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nombre de la conversación en grupo" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descripción de la conversación en grupo" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistente" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "La sala persistirá después de que el último ocupante se salga" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Búsqueda pública" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Los ocupantes pueden cambiar de asunto" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permiso para ver los JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "¿Quién puede ver los JID's de los ocupantes?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Contraseña" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Una contraseña para restringir el acceso de la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderada" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Solo los participantes con voz pueden enviar mensajes" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Solo miembros" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Solo los miembros pueden entrar a la conversación" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historial de mensajes" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Cantidad máxima de mensajes previos emitido por la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuración de la conversación en grupo" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "¡Bienvenido a Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Inicia sesión o crea una cuenta para iniciar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Establecer cuenta" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "No hay cuentas activas" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gestionar cuentas" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Contraseña incorrecta" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "¿Quitar cuenta %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Quitar" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Seleccionar imagen" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imágenes" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Todos los archivos" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conectado" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconectado" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Error" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Añadir Cuenta" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "El servidor no pudo probar que es %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "El certificado no es confiable para el sistema operativo." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "El certificado de seguridad está emitido para otro dominio." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "El certificado de seguridad solo será válido en el futuro." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "El certificado de seguridad está expirado." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Iniciar sesión a %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Ya puedes usar la cuenta %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Usuario o contraseña incorrecta" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Algo ha ido mal" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "No hay respuesta del servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrarte en %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "El servidor requiere registrarse a través de un sitio web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Abrir sitio web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrarse" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Comprobar %s para información sobre como registrarse" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Editar el mensaje" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tú" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Añadir la reacción" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Guardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s y otros %i están tecleando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s y %s están escribiendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s y %s están escribiendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s está escribiendo…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Llamada iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Inició hace %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Ha gestionado esta llamada en otro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Llamada finalizada" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Finalizada a las %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Duración %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Llamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Hay una llamadas perdida" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s tiene una llamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Llamada rechazada" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Has rechazado esta llamada" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s ha rechazado esta llamada" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Llamada fallida" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i horas" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "hace segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "A este contacto le gustaría añadirte a la lista de contactos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Respuesta" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Bajando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofreció: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Archivo ofrecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Archivo ofrecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transferencia de archivo fallida" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Archivo" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar mensaje" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "No tienes conversaciones abiertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Haz clic en + para iniciar un chat o unirte a un canal" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Cuenta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Alias" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Añadir Contacto" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notificar cada nuevo mensaje" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Convertir sonrisas en emoticonos" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Comprobar ortografía" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Un cliente de XMPP moderno" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un cliente de mensajería moderno y libre para escritorio y móvil. " "Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable " "teniendo la privacidad en mente." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Soporta cifrado fin-a-fin a través de OMEMO y OpenPGP y permite configurar " "características relacionadas con la privacidad, como confirmaciones de " "lectura y notificaciones de escritura." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera el historial de mensajes desde el servidor y sincroniza los " "mensajes con otros dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "No hay búsquedas activas" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escribir para iniciar una búsqueda" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "No hay mensajes coincidentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comprueba la ortografía o intenta quitar los filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "General" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversación" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegación" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Saltar a conversación siguiente" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Saltar a conversación anterior" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Cuentas" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "No hay cuentas configuradas" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Añadir una cuenta" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Iniciar Sesión" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crear cuenta" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "No se pudo establecer una conexión segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Conectar" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Elegir un servidor público" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "O especificar la dirección de servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Iniciar sesión" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Elegir otro servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "¡Todo listo!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finalizado" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Pulsar aquí para iniciar una conversación o unirse a un conversación en " #~ "grupo." #~ msgid "No active conversations" #~ msgstr "No hay conversaciones activa" #~ msgid "Main window with conversations" #~ msgstr "Ventana principal con conversaciones" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s y %i otros" #~ msgid "You can now start using %s" #~ msgstr "Puedes empezar a usar %s" #~ msgid "Open Registration" #~ msgstr "Registro abierto" #~ msgid "Save" #~ msgstr "Guardar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s y %s" #~ msgid "%s and %s" #~ msgstr "%s y %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está escribiendo…" #~ msgstr[1] "están escribiendo…" #~ msgid "has stopped typing" #~ msgstr "ha dejado de escribir" #~ msgid "%i search results" #~ msgstr "%i resultados de la búsqueda" #~ msgid "Discover real JIDs" #~ msgstr "Descubrir JIDs reales" #~ msgid "Who may discover real JIDs?" #~ msgstr "¿Quién puede ver los JIDs reales?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Contraseña requerida para entrar en la conversación en grupo. Dejar vacío " #~ "para ninguna" #~ msgid "Failed connecting to %s" #~ msgstr "Fallo conectando con %s" #~ msgid "Join Conference" #~ msgstr "Unirse a Conversación en grupo" #~ msgid "Communicate happiness." #~ msgstr "Comunícate felizmente." #~ msgid "Preferences" #~ msgstr "Preferencias" #~ msgid "Quit" #~ msgstr "Salir" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "El JID debe tener la forma \"usuario@ejemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar dirección del enlace" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Seleccionar todo" #~ msgid "Search" #~ msgstr "Buscar" #~ msgid "Send message marker" #~ msgstr "Enviar confirmación al recibir y al leer los mensajes" #~ msgid "Start Chat" #~ msgstr "Iniciar Conversación" #~ msgid "Request presence updates" #~ msgstr "Solicitar actualizaciones de presencia" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "" #~ "Esto es una notificación de la aplicación. Haz click en el botón para " #~ "descartar" #~ msgid "Join on startup" #~ msgstr "Unirse al inicio" #~ msgid "Add Chat" #~ msgstr "Añadir conversación" dino-0.4.3/main/po/eu.po0000644000000000000000000011326414452563620013476 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-12-09 11:48+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" "Fitxategiak zerbitzariaren kargatzeko gehienezko tamaina gainditzen du." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mezu luzeegia" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editatuta" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "bidaltzeko zain…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "entregak huts egin du" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Enkriptatu gabe" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Ezin da mezua bidali" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "orain dela min %i" msgstr[1] "orain dela %i min" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Orain" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ni" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Irudia bidali da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fitxategia bidali da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Irudia jaso da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fitxategia jaso da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Irteten ari den deia" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Deia jasotzen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Atzo" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Elkarrizketa hasi" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Jabea" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administratzailea" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Kidea" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Erabiltzailea" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Gonbidatu" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Solasaldi pribatua hasi" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kanporatu" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Idazteko baimena eman" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Idazteko baimena ezeztatu" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Konferentziara gonbidatu" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Fitxategia hautatu" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Hautatu" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Utzi" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s hemendik: %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Konferentzia honek ez dizu mezuak bidaltzea baimentzen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Baimena eskatu" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Fitxategi bat bidali" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hasi deia" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audio-deia" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Bideo-deia" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Mezuak bilatu" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Kideak" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Arazte informazioa" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Deika…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Soinua jotzen…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Konektatzen…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s(e)k deia amaitu zuen" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s(e)k uko egin dio deiari" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamerak" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ez da kamerarik aurkitu." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonoak" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ez da mikrofonorik aurkitu." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Bozgorailuak" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ez da bozgorailurik aurkitu." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Dei batera gonbidatu" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Hasi" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Kanalera gehitu" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Hurrengoa" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Batu" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Atzera" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Batzen…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pasahitza behar da gelara sartzeko" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Konferentzia sortu edo batzea debekatuta" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Gela ez da existitzen" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ez duzu gela sortzeko baimenik" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Gela kideentzat da soilik" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Ezizen ezberdin bat hautatu" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Gelak kide gehiegi ditu" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Ezin izan da %s(e)ra konektatu" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Helbide ez balioduna" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Gehitu" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Laster-teklak" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Dinori buruz" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Gaur" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "Bilaketa emaitza %i" msgstr[1] "%i bilaketa emaitza" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "hemen %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "%s(r)ekin" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Bideo-deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Taldeko bideo-deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Taldeko deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Ukatu" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Onartu" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Harpidetzaren eskaera" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Ukatu" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "%s(e)ra gonbidapena" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s(e)k %s(e)ra gonbidatu zaitu" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Baimen eskaera" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s baimena eskatzen du %s(e)n idazteko" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Ezarpenak" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Bertako ezarpenak" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Idazte jakinarazpenak bidali" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Bidali irakurtze baieztapena" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Jakinarazpenak" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Piztuta" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Itzalita" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Soilik aipatua izaterakoan" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Lehenetsia: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Eskatu" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Baimenak" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Mezuak bidaltzeko baimena eskatu" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferentziaren xehetasunak" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktuaren xehetasunak" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blokeatu" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Komunikazioa eta egoeraren eguneraketak edozein norabideetan blokeatuta daude" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Gelaren izena" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Gelaren deskribapena" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Iraunkorra" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Gelak iraun egingo du azken parte-hartzailea irten ondoren" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Publikoki bilagarria" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Parte-hartzaileek gaia alda dezakete" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "JIDak ikusteko baimena" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Nor dago baimenduta parte-hartzaileen JIDak ikusteko?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Pasahitza" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Gelarako sarbidea murrizteko pasahitza" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderatua" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Ahotsa duten parte-hartzaileek soilik bidali dezakete mezuak" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Kideak soilik" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Kideak soilik sar daitezke gelara" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Mezuen historia" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Gelak itzuliko dituen gehienezko historiako mezuak" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Gelaren konfigurazioa" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Ongi etorri Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Saioa hasi edo kontu bat sortu hasteko." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Kontu bat ezarri" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Ez dago kontu aktiborik" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Kontuak kudeatu" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Pasahitz okerra" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "TLS ziurtagiri ez balidouna" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "%s kontua kendu?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Kendu" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Irudia hautatu" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Irudiak" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Fitxategi guztiak" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Konektatuta" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Ez konektatuta" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Akatsa" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Kontua gehitu" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Zerbitzariak ezin izan du frogatu %s dela." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Zure sistema eragilea ez da bere segurtasun ziurtagiriaz fido." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Bere segurtasun ziurtagiria beste domeinu baterako eman da." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Bere segurtasun ziurtagiria etorkizunean izango da baliozkoa soilik." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Bere segurtasun ziurtagiria iraungi da." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Hasi saioa %s(e)n" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Orain %s kontua erabili dezakezu." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Erabiltzaile izen edo pasahitz okerra" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Zerbait oker joan da" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Erantzunik ez zerbitzaritik" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Izena eman hemen: %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Zerbitzariak webgune baten bidez izena ematea eskatzen du" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Webgunea ireki" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Izena eman" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ikusi %s izena ematearen inguruko informazioa lortzeko" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ireki" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Gorde honela…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s eta beste %i idatzen ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s eta %s idazten ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s eta %s idazten ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s idazten ari da…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Deia hasi da" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Duela %s hasi zen" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Deia hau beste gailu batean kudeatu duzu" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Deia amaitu da" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "%s-tan amaituta" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "%s iraun zuen" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Dei galdua" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Dei hau galdu duzu" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s(e)k dei hau galdu du" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Deia atzera bota da" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Dei horri uko egin diozu" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s(e)k uko egin dio dei horri" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Deia huts egin da" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "ordu %i" msgstr[1] "%i ordu" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "minutu %i" msgstr[1] "%i minutu" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "duela segundo batzuk" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Kontaktu honek bere kontaktuen zerrendara gehitu nahi zaitu" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "%s deskargatzen…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s(e)k eskeini du: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fitxategia eskeini da: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fitxategia eskeinita" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Fitxategiaren transmisioak huts egin du" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fitxategia" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Mezua eguneratu" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Ez duzu irekitako txatik" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Kontuak" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Goitizena" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Ezizena" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontaktua gehitu" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Mezu berri bat heltzerakoan jakinarazi" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Irrifartxoak emotikono bihurtu" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Ortografia egiaztatu" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "XMPP txat bezero modernoa" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP " "esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan " "hartzeaz gain." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta " "pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta " "idazketa jakinarazpenak bezala." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak " "sinkronizatzen ditu." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ez dago bilaketa aktiborik" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Idatzi bilatzen hasteko" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ez dago bat egiten duen mezurik" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ortografia egiaztatu edo saiatu iragazkiak kentzen" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Bidali" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Orokorra" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Elkarrizketa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Nabigazioa" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Jauzi hurrengo elkarrizketara" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Jauzi aurreko elkarrizketara" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Kontuak" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Bertako ezizena" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Ez dago konfiguratutako konturik" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Kontu bat gehitu" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Saioa hasi" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Kontua sortu" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Ezin izan da konexio seguru bat ezarri" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Konektatu" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Zerbitzari publiko bat hautatu" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Edo zerbitzari baten helbidea zehaztu" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Saioa hasi" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Beste zerbitzari bat hartu" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Guztia ezarri da!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Amaitu" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klikatu hemen elkarrizketa berri bat hasi edo kanal batean sartzeko." #~ msgid "No active conversations" #~ msgstr "Ez dago solasaldi aktiborik" #~ msgid "Main window with conversations" #~ msgstr "Leiho nagusia elkarrizketekin" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s eta beste %i" #~ msgid "You can now start using %s" #~ msgstr "Orain %s erabiltzen hasi zaitezke" #~ msgid "Open Registration" #~ msgstr "Izen emate irekia" #~ msgid "Save" #~ msgstr "Gorde" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s eta %s" #~ msgid "%s and %s" #~ msgstr "%s eta %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "idazten ari da…" #~ msgstr[1] "idazten ari dira…" #~ msgid "has stopped typing" #~ msgstr "idazteari utzi dio" #~ msgid "%i search results" #~ msgstr "Bilaketaren emaitzak: %i" #~ msgid "Discover real JIDs" #~ msgstr "Egiazko JIDak ikusi" #~ msgid "Who may discover real JIDs?" #~ msgstr "Nork ikus ditzake egiazko JIDak?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Gelan sartzeko beharrezko pasahitza, utzi zuriz pasahitzik ez ezartzeko" #~ msgid "Failed connecting to %s" #~ msgstr "Huts %s(e)ra konektatzerakoan" #~ msgid "Join Conference" #~ msgstr "Konferentziara batu" #~ msgid "Communicate happiness." #~ msgstr "Poztasuna komunikatu." #~ msgid "Preferences" #~ msgstr "Hobespenak" #~ msgid "Quit" #~ msgstr "Irten" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JIDak \"erabiltzailea@eredua.com\" itxura izan beharko luke" #~ msgid "Copy Link Address" #~ msgstr "Loturaren helbidea kopiatu" #~ msgid "Copy" #~ msgstr "Kopiatu" #~ msgid "Select All" #~ msgstr "Dena hautatu" #~ msgid "Search" #~ msgstr "Bilatu" #~ msgid "Send message marker" #~ msgstr "Mezu marka bidali" #~ msgid "Start Chat" #~ msgstr "Berriketa hasi" #~ msgid "Request presence updates" #~ msgstr "Presentzia eguneraketak eskatu" #~ msgid "Join on startup" #~ msgstr "Hasieran batu" #~ msgid "Add Chat" #~ msgstr "Berriketa gehitu" dino-0.4.3/main/po/fa.po0000644000000000000000000011233514452563620013451 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-12 11:00+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "حجم فایل از حداکثر حجم بارگذاری سرور بیشتر است." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "پیام خیلی طولانی است" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "ویرایش شده" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "رمزگذاری نشده" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "ارسال پیام ممکن نیست" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i دقیقه پیش" msgstr[1] "%i دقیقه پیش" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "همین الان" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "من" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "تصویر ارسال شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "فایل ارسال شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "تصویر دریافت شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "فایل دریافت شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "دیروز" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "آغاز گفتگو" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "مالک" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "مدیر" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "عضو" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "کاربر" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "دعوت" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "گفتگوی خصوصی را شروع کنید" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "بیرون کردن" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "مجوز نوشتن بدهید" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "مجوز نوشتن را لغو کنید" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "دعوت کردن به کنفرانس" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "انتخاب فایل" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "انتخاب" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "لغو" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s از %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "این کنفرانس به شما اجازهٔ ارسال پیام نمی دهد." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "درخواست مجوز" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "ارسال فایل" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "جست‌جوی پیام‌ها" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "اعضا" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "در حال اتصال…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "آغاز" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "پیوستن به کانال" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "بعدی" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "پیوستن" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "بازگشت" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "در حال پیوستن…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "گذرواژه برای ورود به اتاق لازم است" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "از پیوستن به کنفرانس یا ساختن آن منع شده‌اید" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "اتاق وجود ندارد" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "مجاز به ایجاد اتاق نیست" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "اتاق مخصوص اعضا" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "یک نام دیگر انتخاب کنید" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "تعداد زیادی از افراد در اتاق هستند" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "اتصال با %s برقرار نشد" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "آدرس نامعتبر" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "اضافه کردن" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "میانبر های صفحه کلید" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "دربارهٔ Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "امروز" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a , %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i نتیجه جست وجو" msgstr[1] "%iنتایج جست وجو" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "در %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "با %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "رد کردن" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "پذیرش" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "درخواست اشتراک" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "رد کردن" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "دعوت به %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s شما را به %s دعوت کرد" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "درخواست مجوز" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s درخواست نوشتن در %s را دارد" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "تنظیمات" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "تنظیمات محلی" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "ارسال اعلان تایپ کردن" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "فرستادن رسید خوانده‌شدن" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "اعلان‌ها" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "روشن" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "خاموش" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "فقط وقتی ذکر شده" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "پیش‌فرض: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "درخواست" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "مجوزها" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "درخواست مجوز برای ارسال پیام" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "جزئیات کنفرانس" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "اطلاعات مخاطب" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "مسدود کردن" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "ارتباط و به‌روز رسانی وضعیت در هر دو جهت مسدود شده است" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "نام اتاق" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "توضیحات اتاق" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "مداوم" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "بعد از رفتن آخرین فرد اتاق دوام خواهد داشت" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "قابلیت جستجوی عمومی" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "افراد ممکن است موضوع را تغییر دهند" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "اجازه مشاهده JIDها" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "چه کسی مجاز است JIDهای افراد را مشاهده کند؟" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "رمز" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "رمز برای محدود کردن دسترسی به اتاق" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "تعدیل شده" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "فقط افراد دارای صدا می توانند پیام ارسال کنند" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "فقط اعضا" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "فقط اعضا می توانند وارد اتاق شوند" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "تاریخچه پیام" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "حداکثر مقدار عقب مانده ارسال شده توسط اتاق" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "پیکربندی اتاق" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "به دینو خوش آمدید!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "برای شروع یک حساب بسازید یا وارد شوید." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "حساب تنظیم کنید" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "حساب فعالی وجود ندارد" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "مدیریت حساب‌ها" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "رمز اشتباه است" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "گواهی تی‌ال‌اس نامعتبر است" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "حساب %s حذف شود؟" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "حذف" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "انتخاب آواتار" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "تصاویر" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "تمامی فایل‌ها" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "متصل" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "قطع شده" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "خطا" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "اضافه کردن حساب" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "سرور نمی تواند اثبات کند که %s است." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "گواهی امنیتی آن مورد اعنماد سیستم عامل شما نیست." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "گواهی امنیتی آن برای دامنه دیگری صادر شده است." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "گواهی امنیتی آن فقط در آینده معتبرخواهد شد." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "گواهی امنیتی آن منقضی شده است." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "ورود به %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "اکنون می توانید از این حساب استفاده کنید: %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "نام کاربری یا رمز اشتباه است" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "مشکلی پیش آمد" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "هیچ پاسخی از طرف سرور وجود ندارد" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "ثبت نام در %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "سرور نیاز به ثبت نام از طریق یک وب سایت دارد" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "بازکردن وب‌سایت" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "ثبت نام" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "بررسی %s برای کسب اطلاعات در مورد نحوه ثبت نام" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s، %s و %i نفر دیگر در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s، %s و %s در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s و %s در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s در حال نوشتن است…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "این مخاطب می‌خواهد شما را به لیست مخاطبین خود اضافه کند" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "در حال دانلود %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ارائه شده: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "فایل ارائه شده: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "فایل ارائه شده است" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "انتقال فایل انجام نشد" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "به‌روز کردن پیام" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "شما هیچ گپ بازی ندارید" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "حساب" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "نام مستعار" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "نام مستعار" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "افزودن مخاطب" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "اعلان در هنگام رسیدن پیام جدید" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "تبدیل صورتک‌ها به ایموجی" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "بررسی املا" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "کلاینت نوین گپ XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای " "شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم " "قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و " "اعلان در حال نوشتن بودن." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی " "می‌کند." #: main/data/global_search.ui:27 msgid "No active search" msgstr "جستجوی فعالی وجود ندارد" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "برای شروع جستجو تایپ کنید" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "هیچ پیام مطابقی وجود ندارد" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "املا را بررسی کنید یا فیلترها را حذف کنید" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "فرستادن" #: main/data/shortcuts.ui:10 msgid "General" msgstr "فراگیر" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "گفتگو" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "جهت‌یابی" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "رفتن به گفت‌گوی بعدی" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "رفتن به گفت‌گوی قبلی" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "حساب‌ها" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "نام مستعار محلی" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "هیچ حسابی پیکربندی نشده است" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "اضافه کردن حساب" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "ورود" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "ساخت کاربری" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "اتصال ایمن برقرار نشد" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "اتصال" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "یک سرور عمومی انتخاب کنید" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "یا آدرس سرور را مشخص کنید" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "در عوض وارد شوید" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "سرور دیگری انتخاب کنید" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "همه تنظیم شده!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "اتمام" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "برای شروع گفتگو یا پیوستن به کانال اینجا کلیک کنید." #~ msgid "No active conversations" #~ msgstr "هیچ گفتگوی فعالی نیست" dino-0.4.3/main/po/fi.po0000644000000000000000000010540514452563620013461 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-12 20:01+0000\n" "Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Viesti liian pitkä" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "muokattu" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Salaamaton" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Viestiä ei voi lähettää" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min sitten" msgstr[1] "%i min sitten" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Juuri nyt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Minä" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Kuva lähetetty" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Tiedosto lähetetty" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Kuva vastaanotettu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Tiedosto vastaanotettu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Eilen" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Aloita keskustelu" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Omistaja" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Ylläpitäjä" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Jäsen" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Käyttäjä" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Kutsu" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Aloita yksityiskeskustelu" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Potkaise pihalle" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Kutsu ryhmäkeskusteluun" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Valitse" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Peruuta" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Yhdistää…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Aloita" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Liity kanavalle" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Seuraava" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Liity" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Takaisin" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Yhdistää…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Huoneeseen vaaditaan salasana" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Estetty liittymästä tai luomasta ryhmäkeskustelua" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Huonetta ei ole olemassa" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Huoneen luomista ei sallita" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Huone vain jäsenille" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Valitse jokin muu nimimerkki" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Liikaa käyttäjiä huoneessa" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, fuzzy, c-format msgid "Could not connect to %s" msgstr "Ei voitu yhdistää: %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Väärä osoite" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Lisää" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Tänään" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Hyväksy" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Yhteyspyyntö" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Hylkää" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, fuzzy, c-format msgid "Invitation to %s" msgstr "Kutsu %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, fuzzy, c-format msgid "%s invited you to %s" msgstr "%s on kutsunut sinut %s-ryhmään" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Asetukset" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Paikalliset asetukset" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Lähetä kirjoittamisilmoitukset" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Lähetä lukukuittaukset" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Ilmoitukset" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Käytössä" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Pois" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Vain mainittaessa" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Oletus: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Ryhmäkeskustelun yksityiskohdat" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Yhteystiedot" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Estä" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Viestit ja tilapäivitykset estetään molempiin suuntiin" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Huoneen nimi" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Huoneen kuvaus" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Pysyvä" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Huone säilyy viimeisen käyttäjän poistuttua" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Julkisesti löydettävissä" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Käyttäjät voivat vaihtaa aihetta" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Salasana" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderoitu" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Vain äänen saaneet käyttäjät voivat lähettää viestejä" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Vain jäsenille" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Vain jäsenet pääsevät huoneeseen" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Viestihistoria" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Säilytettyjen viestien enimmäismäärä huoneessa" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Huoneen asetukset" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Tervetuloa Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Kirjaudu sisään tai luo tili päästäksesi alkuun." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Ei aktiivisia tilejä" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Hallitse tilejä" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Väärä salasana" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "TLS-varmenne ei kelpaa" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Poista tili %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Poista" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Valitse keskustelukuvake" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Kuvat" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Kaikki tiedostot" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Yhdistetty" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Ei yhdistetty" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Virhe" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Lisää tili" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, fuzzy, c-format msgid "Sign in to %s" msgstr "Kirjaudu sisään %s-ryhmään" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Väärä käyttäjätunnus tai salasana" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Jokin meni pieleen" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Palvelin ei vastaa" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Palvelin vatii kirjautumaan verkkosivuston kautta" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Rekisteröity" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Tarkista %s ohjeet kirjautumiseen" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, fuzzy, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ja %i muuta kirjoittavat" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ja %s kirjoittavat…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s ja %s kirjoittavat…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s kirjoittaa…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Tämä käyttäjä haluaisi lisätä sinut yhteystietoihinsa" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Lataa %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Tiedosto" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Tili" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nimimerkki" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Lisää yhteystieto" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Ilmoita kun uusi viesti saapuu" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Muuta hymiöt emojeiksi" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 #, fuzzy msgid "Modern XMPP Chat Client" msgstr "Moderni XMPP-asiakasohjelma" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino on nykyaikainen avoimen lähdekoodin jutteluohjelma työpöydälle. Se " "keskittyy tarjoamaan selkeän ja luotettavan Jabber/XMPP-kokemuksen " "unohtamatta yksityisyyttäsi." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja " "mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten " "ja kirjoitusilmoitusten asetusten määrittämisen." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino hakee historian palvelimelta ja synkronisoi viestit muiden laitteiden " "kanssa." #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Tilit" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Paikallinen alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Ei asetettuja tilejä" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Lisää tili" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" #~ msgid "No active conversations" #~ msgstr "Ei aktiivisia keskusteluja" #~ msgid "Main window with conversations" #~ msgstr "Keskustelut pääikkunassa" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s and %i muuta" #~ msgid "Save" #~ msgstr "Tallenna" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ja %s" #~ msgid "%s and %s" #~ msgstr "%s ja %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "kirjoittaa…" #~ msgstr[1] "kirjoittavat…" #~ msgid "has stopped typing" #~ msgstr "on lakannut kirjoittamasta" #~ msgid "Discover real JIDs" #~ msgstr "Näytä oikeat JID:t" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kuka voi nähdä oikeat JID:t?" #~ msgid "Password required for room entry, if any" #~ msgstr "Huoneeseen vaadittava salasana, jos asetettu" #~ msgid "Join Conference" #~ msgstr "Liity ryhmäkeskusteluun" #~ msgid "Preferences" #~ msgstr "Asetukset" #~ msgid "Quit" #~ msgstr "Lopeta" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Anna JID muodossa \"user@example.com\"" #~ msgid "Copy Link Address" #~ msgstr "Kopioi linkin osoite" #~ msgid "Copy" #~ msgstr "Kopioi" #~ msgid "Select All" #~ msgstr "Valitse kaikki" #~ msgid "Search" #~ msgstr "Etsi" #~ msgid "Send message marker" #~ msgstr "Lähetä lukukuittaus" #~ msgid "Start Chat" #~ msgstr "Aloita keskustelu" dino-0.4.3/main/po/fr.po0000644000000000000000000011530214452563620013467 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-02-06 10:39+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Le fichier dépasse la taille maximale autorisée par le serveur." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Message trop long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "modifié" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "en cours d’envoi…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "échec de l’envoi" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Non chiffré" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Impossible d’envoyer le message" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "le %x à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "le %x à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "le %d %b à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "le %d %b à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Il y a %i minute" msgstr[1] "Il y a %i minutes" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "À l’instant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Moi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Image envoyée" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fichier envoyé" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Image reçue" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fichier reçu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Appel sortant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Appel entrant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Hier" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Commencer une discussion" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propriétaire" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrateur" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilisateur" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviter" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Commencer une discussion privée" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Éjecter du salon" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Accorder la permission d’écrire" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Révoquer la permission d’écrire" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviter dans ce salon" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Sélectionner un fichier" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Sélectionner" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annuler" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Vous n’êtes pas autorisé(e) à envoyer de messages sur ce salon." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demander la permission" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Envoyer un fichier" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Démarrer un appel" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Appel audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Appel vidéo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Rechercher dans les messages" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informations de débogage" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Appel en cours…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Ça sonne…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Connexion…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s a arrêté l’appel" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s a rejeté cet appel" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Caméras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Aucune caméra trouvée." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microphones" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Aucun microphone trouvé." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Haut-parleurs" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Aucun haut-parleur trouvé." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviter à un appel" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Démarrer" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Rejoindre un salon" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Suivant" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Rejoindre" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Retour" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Connexion au salon…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Un mot de passe est nécessaire pour rejoindre ce salon" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Vous n’avez pas le droit de rejoindre ou de créer ce salon" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Ce salon n’existe pas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Création de salon interdite" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Salon réservé aux membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Choisissez un autre pseudo" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Trop de participants dans ce salon" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Impossible de se connecter à %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Adresse invalide" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ajouter" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "À propos de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Aujourd’hui" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i résultat" msgstr[1] "%i résultats" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Sur %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Avec %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Appel vidéo entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Appel vidéo de groupe entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Appel de groupe entrant" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejet" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accepter" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Demande d’ajout de contact" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Refuser" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitation à %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s vous a invité(e) à rejoindre le salon %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Demande de permission" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demande la permission d’écrire dans %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Paramètres" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Paramètres locaux" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Envoyer les notifications d’écriture" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Envoyer les accusés de réception" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notifications" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Épingler la conversation" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Épingle la conversation en haut de la liste des conversations" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Activé" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Désactivé" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Seulement quand mentionné" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Par défaut : %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Demander" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permissions" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Demander la permission d’envoyer des messages" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Informations du salon" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Informations du contact" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloquer" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Tous les échanges de messages et de statuts sont bloqués" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nom du salon" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Description du salon" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistant" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Ce salon persistera après le départ du dernier participant" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Visible publiquement" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Les participants peuvent changer le sujet" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Autorisation de voir les JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui peut voir les JIDs des participants ?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Mot de passe" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Un mot de passe pour restreindre l’accès au salon" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Modéré" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" "Seuls les participants avec la permission d’écrire peuvent envoyer des " "messages" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Membres uniquement" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Seuls les membres peuvent accéder au salon" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historique des messages" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Nombre maximum de messages d’historique renvoyé par le salon" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuration du salon" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Bienvenue dans Dino !" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Connectez-vous ou créez un compte pour commencer." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurer un compte" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Aucun compte actif" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gérer les comptes" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Mauvais mot de passe" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificat TLS invalide" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Supprimer le compte %s ?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Supprimer" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Sélectionner un avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Images" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Tous les fichiers" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Connecté" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Déconnecté" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Erreur" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Ajouter un compte" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Le serveur n’a pas su prouver qu’il est %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Votre système d’exploitation ne fait pas confiance au certificat proposé." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Son certificat a été généré pour un autre domaine." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Son certificat ne deviendra valide que dans le futur." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Son certificat a expiré." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Se connecter en tant que %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Vous pouvez maintenant utiliser le compte %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Mauvais nom d’utilisateur ou mot de passe" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Quelque chose s’est mal passé" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Pas de réponse du serveur" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "S’inscrire sur %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Ce serveur nécessite de s’inscrire depuis un site Web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Ouvrir le site Web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "S’inscrire" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Consultez %s pour plus d’informations sur comment s’inscrire" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Éditer le message" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Vous" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Ajouter une réaction" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ouvrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Enregistrer sous…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s et %i autres sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s et %s sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s et %s sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s est en train d’écrire…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Appel démarré" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "A commencé il y a %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Vous avez réagi à cet appel depuis un autre client" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Appel terminé" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "S’est terminé à %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "A duré %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Appel manqué" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Vous avez manqué cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s a manqué cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Appel rejeté" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Vous avez rejeté cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s a rejeté cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Appel échoué" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i heure" msgstr[1] "%i heures" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minute" msgstr[1] "%i minutes" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "quelques secondes" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Cette personne souhaiterait vous ajouter à sa liste de contacts" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Répondre" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Téléchargement de %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s proposé au téléchargement : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fichier proposé au téléchargement : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fichier proposé au téléchargement" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Échec du transfert de fichier" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fichier" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Modifier ce message" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vous n’avez aucune discussion ouverte" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Cliquez sur + pour commencer une conversation ou rejoindre un salon" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Pseudo" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Ajouter un contact" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notifier de l’arrivée de nouveaux messages" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Convertir les émoticônes typographiques en emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Vérifier l'orthographe" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Client de clavardage XMPP moderne" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino est un client de clavardage libre et moderne pour le bureau. Il se " "concentre sur la fourniture d’une expérience XMPP simple et fiable tout en " "ayant toujours à l’esprit votre confidentialité." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et " "permet de configurer les fonctions liées à la confidentialité telles que les " "accusés de réception et les notifications d’écriture." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino récupère l’historique du serveur et synchronise les messages avec les " "autres clients." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Pas de recherche en cours" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Écrivez pour lancer une recherche" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Aucun message correspondant à la recherche" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Vérifiez l’orthographe ou essayez de supprimer des filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Envoyer" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Général" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Discussion" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigation" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Aller à la discussion suivante" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Revenir à la discussion précédente" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Comptes" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Aucun compte configuré" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Ajouter un compte" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "S’identifier" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Créer un compte" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Impossible d’établir une connexion sécurisée" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connexion" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Choisissez un serveur public" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ou indiquez l’adresse d’un serveur" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Annuler et se connecter" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Choisir un autre serveur" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tout est prêt !" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Terminer" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Cliquez ici pour commencer une discussion ou rejoindre un salon." #~ msgid "No active conversations" #~ msgstr "Aucune discussion en cours" #~ msgid "Main window with conversations" #~ msgstr "Fenêtre principale avec des conversations" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s et %i autres" #~ msgid "You can now start using %s" #~ msgstr "Vous pouvez maintenant utiliser %s" #~ msgid "Open Registration" #~ msgstr "Inscription ouverte" #~ msgid "Save" #~ msgstr "Sauver" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s et %s" #~ msgid "%s and %s" #~ msgstr "%s et %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "est en train d’écrire…" #~ msgstr[1] "sont en train d’écrire…" #~ msgid "has stopped typing" #~ msgstr "a arrêté d’écrire" #~ msgid "%i search results" #~ msgstr "%i résultats de recherche" #~ msgid "Discover real JIDs" #~ msgstr "Découverte des vrais JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui peut découvrir les vrais JID ?" #~ msgid "Password required for room entry, if any" #~ msgstr "Mot de passe pour rejoindre le salon, vide pour aucun" #~ msgid "Failed connecting to %s" #~ msgstr "Échec de la connexion à %s" #~ msgid "Join Conference" #~ msgstr "Rejoindre une conférence" #~ msgid "Communicate happiness." #~ msgstr "Communiquer avec bonheur." #~ msgid "Preferences" #~ msgstr "Préférences" #~ msgid "Quit" #~ msgstr "Quitter" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Le JID doit être de la forme « utilisateur@example.com »" #~ msgid "Copy Link Address" #~ msgstr "Copier l’adresse du lien" #~ msgid "Copy" #~ msgstr "Copier" #~ msgid "Select All" #~ msgstr "Tout sélectionner" #~ msgid "Search" #~ msgstr "Rechercher" #~ msgid "Send message marker" #~ msgstr "Envoyer les marqueurs de message" #~ msgid "Start Chat" #~ msgstr "Commencer une discussion" #~ msgid "Request presence updates" #~ msgstr "Demander les mises à jour de la présence" #~ msgid "Join on startup" #~ msgstr "Rejoindre au démarrage" #~ msgid "Add Chat" #~ msgstr "Ajouter discussion" dino-0.4.3/main/po/gl.po0000644000000000000000000011366714452563620013476 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "O ficheiro excede o tamaño máximo de subida permitido." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mensaxe demasiado longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "pendente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "fallou a entrega" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Non cifrado" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Imposible enviar a mensaxe" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fai %i min" msgstr[1] "fai %i minutos" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Xusto agora" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imaxe enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Ficheiro enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imaxe recibida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Ficheiro recibido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Chamada saínte" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Chamada entrante" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Onte" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Comezar conversa" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuario" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Comezar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Botar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permiso para escribir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar o permiso de escritura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar ó grupo" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Escolle ficheiro" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Escoller" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Non tes permiso para escribir en esta sala de conferencia." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicita permiso" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Enviar ficheiro" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Buscar mensaxes" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membresía" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Información de depuración" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Chamando…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Soando…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conectando…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s rematou a chamada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s rexeitou a chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Cámaras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Non se atopa unha cámara." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Micrófonos" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Non se atopa o micrófono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altofalantes" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Non se atopa o altofalante." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar a Chamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Comezar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Unirse a unha canle" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Seguinte" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Unirse" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Atrás" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Uníndose…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Precisas dun contrasinal para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Non ten permiso para unirse ou crea-lo grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "A sala non existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Non pode crea-la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala só para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolle un alcume diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Demasiada xente na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Non se conectou a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Enderezo non válido" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Engadir" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Atallos de teclado" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Acerca de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoxe" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da procura" msgstr[1] "%i resultados da procura" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Chamada de vídeo entrante" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Chamada de vídeo en grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Chamada en grupo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rexeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Solicitude de subscrición" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Rexeitar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Convite a %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s convidoute a %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Solicitude de permiso" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s pide permiso para escribir en %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Axustes" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Axustes locais" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Mostrar que estás escribindo" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Enviar confirmacións de lectura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificacións" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Fixar a conversa" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Fixa a conversa na parte superior da lista de conversas" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Activado" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Desactivado" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Só cando te mencionan" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Por defecto: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Solicitar" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permisos" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Solicita permiso para enviar mensaxes" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalles do grupo" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalles do contacto" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloquear" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "As actualizacións do estado e comunicación están bloqueadas en calquera " "dirección" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrición da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistente" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "A sala permanecerá despois de que saia o derradeiro participante" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Pódese atopar de xeito público" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Os participantes poden muda-lo asunto" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permiso para ollar JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quen pode ollar os JIDs dos participantes?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Contrasinal" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Un contrasinal para restrinxir o acceso á sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderado" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Só os participantes con voz poden enviar mensaxes" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Só membros" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Só os membros poden entrar na sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Histórico das mensaxes" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Morea máxima de rexistros fornecidos pola sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Axustes da sala" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Benvida a Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Acceder ou crear unha conta para comezar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurar conta" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Sen contas activas" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Xestionar contas" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Contrasinal incorrecto" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificado TLS non válido" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Eliminar a conta %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Eliminar" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Escoller imaxe do perfil" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imaxes" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Tódolos ficheiros" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conectado" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconectado" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Erro" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Engadir conta" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor non puido probar que é %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O teu sistema non estableceu a confianza no seu certificado de seguridade." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "O seu certificado de seguridade foi proporcionado para outro dominio." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "O seu certificado de seguridade só terá validez no futuro." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "O certificado de seguridade caducou." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Acceder con %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Agora é posible usar a conta %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Usuario ou contrasinal incorrectos" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Algo fallou" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Sen resposta do servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Rexistrarse en %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "O servidor precisa que te rexistres a través dun sitio web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Abrir sitio web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Rexistrar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Olla en %s para máis información sobre o rexistro" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Editar mensaxe" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ti" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Engadir reacción" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Gardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e outros %i están escribindo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s están a escribir…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s están a escribir…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s está a escribir…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Comezou a chamada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Iniciada hai %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Atendeches a chamada noutro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Chamada finalizada" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Rematou ás %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Durou %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Chamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Perdeches esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Chamada rexeitada" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Rexeitaches esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s rexeitou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Fallou a chamada" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i horas" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "uns poucos segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Esta persoa gostaríalle engadirte á súa listaxe dos contactos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Responder" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Descargando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofreceuche: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Ficheiro ofrecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Ficheiro ofrecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Fallou a transferencia do ficheiro" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Ficheiro" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar mensaxe" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Non tes conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Preme no + para comezar unha conversa ou unirte a unha canle" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Sobrenome" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alcume" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Engadir contacto" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notificar cando chega unha nova mensaxe" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Converter risoños a emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Comprobar ortografía" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Cliente moderno para conversas XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é un cliente moderno e de código aberto para o escritorio. Orientado a " "fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e " "seguranza presentes." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suporta o cifrado de punto-a-punto con OMEMO e OpenPGP e permite configurar " "trazos orientados á privacidade tales coma confirmación de lectura e " "notificacións de escritura." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros " "dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sen procura activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escriba para comezar unha procura" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sen mensaxes coincidentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comprobe a escrita ou tente eliminar filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Xeral" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegación" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ir a seguinte conversa" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ir a conversa anterior" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Contas" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alcume local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Sen contas configuradas" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Engadir unha conta" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Acceder" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crear unha conta" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Non se estableceu unha conexión segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Acceder" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Escoller un servidor público" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ou indicar un enderezo de servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Utiliza conta existente" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Escoller outro servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Todo feito!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Rematar" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Preme aquí para iniciar unha conversa ou unirte a unha canle." #~ msgid "Video call incoming" #~ msgstr "Chamada de vídeo entrante" #~ msgid "Call incoming" #~ msgstr "Chamada entrante" #~ msgid "Establishing call" #~ msgstr "Establecendo chamada" #~ msgid "Video call establishing" #~ msgstr "Establecendo chamada de vídeo" #~ msgid "Call establishing" #~ msgstr "Establecendo chamada" #~ msgid "Call in progress…" #~ msgstr "Chamada en curso…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Durou %s" #~ msgid "seconds" #~ msgstr "segundos" #~ msgid "No active conversations" #~ msgstr "Sen conversas activas" #~ msgid "Main window with conversations" #~ msgstr "Lapela principal con conversas" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i outros" #~ msgid "You can now start using %s" #~ msgstr "Agora xa podes comezar a utilizar %s" #~ msgid "Open Registration" #~ msgstr "Rexistro aberto" #~ msgid "Save" #~ msgstr "Gardar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está a escribir…" #~ msgstr[1] "están a escribir…" #~ msgid "has stopped typing" #~ msgstr "deixou de escribir" #~ msgid "%i search results" #~ msgstr "%i resultados da procura" #~ msgid "Discover real JIDs" #~ msgstr "Descobri-los JIDs reais" #~ msgid "Who may discover real JIDs?" #~ msgstr "Quen pode descobri-los JIDs reais?" #~ msgid "Password required for room entry, if any" #~ msgstr "Contrasinal precisada para entrar na sala, se houbese" #~ msgid "Failed connecting to %s" #~ msgstr "Ocorreu un erro ó conectar cara %s" #~ msgid "Join Conference" #~ msgstr "Unirse a un grupo" #~ msgid "Communicate happiness." #~ msgstr "Espalla a felicidade." #~ msgid "Preferences" #~ msgstr "Preferencias" #~ msgid "Quit" #~ msgstr "Saír" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "O JID debe te-lo formato \"usuario@exemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar enderezo da ligazón" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Escoller todo" dino-0.4.3/main/po/hu.po0000644000000000000000000011432514452563620013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "A fájl meghaladja a kiszolgáló legnagyobb feltöltési méretét." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Az üzenet túl hosszú" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "szerkesztve" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "függőben…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "sikertelen kézbesítés" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Titkosítatlan" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Nem lehet elküldeni az üzenetet" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b. %e., %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b. %e., %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i perccel ezelőtt" msgstr[1] "%i perccel ezelőtt" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Épp most" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Én" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Kép elküldve" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fájl elküldve" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Kép érkezett" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fájl érkezett" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Kimenő hívás" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Bejövő hívás" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b. %e." #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Tegnap" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Beszélgetés indítása" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Tulajdonos" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Adminisztrátor" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Tag" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Felhasználó" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Meghívás" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Magánbeszélgetés indítása" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kirúgás" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Írási jogosultság megadása" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Írási jogosultság visszavonása" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Meghívás a konferenciába" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Fájl kiválasztása" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Kiválasztás" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Mégse" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s tőle: %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ez a konferencia nem engedélyezi az üzenetek küldését." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Jogosultság kérése" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Fájl küldése" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hívás indítása" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hanghívás" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videohívás" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Üzenetek keresése" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Tagok" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Hibakeresési információk" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Hívás…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Csörgetés…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Kapcsolódás…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s befejezte a hívást" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s elutasította a hívást" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamerák" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nem található kamera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonok" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nem található mikrofon." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hangszórók" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nem található hangszóró." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Meghívás hívásra" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Indítás" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Csatlakozás csatornához" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Következő" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Csatlakozás" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Vissza" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Csatlakozás…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Jelszó szükséges a szobába való belépéshez" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" "Eltiltva a konferenciához való csatlakozástól vagy a konferencia " "létrehozásától" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "A szoba nem létezik" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Nem engedélyezett a szoba létrehozása" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Csak tagoknak engedélyezett szoba" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Válasszon másik becenevet" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Túl sok résztvevő van a szobában" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Nem sikerült kapcsolódni ehhez: %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Érvénytelen cím" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Hozzáadás" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Gyorsbillentyűk" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "A Dino névjegye" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Ma" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%b. %e., %a" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i keresési eredmény" msgstr[1] "%i keresési eredmény" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Ebben: %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Ezzel: %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Bejövő videohívás" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Bejövő csoportos videohívás" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Bejövő csoportos hívás" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Elutasítás" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Elfogadás" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Feliratkozási kérés" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Elutasítás" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Meghívás ide: %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s meghívta Önt ide: %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Jogosultsági kérés" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s jogosultságot kér, hogy ide írjon: %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Beállítások" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Helyi beállítások" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Gépelési értesítések küldése" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Olvasási visszaigazolások küldése" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Értesítések" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Beszélgetés kitűzése" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Kitűzi a beszélgetést a beszélgetéslista tetejére" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Be" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Ki" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Csak ha említenek" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Alapértelmezett: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Kérés" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Jogosultságok" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Jogosultság kérése az üzenetek küldéséhez" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferencia részletei" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Partner részletei" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Tiltás" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "A kommunikáció és az állapotfrissítések mindkét irányból le vannak tiltva" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "A szoba neve" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "A szoba leírása" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Állandó" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "A szoba megmarad azután is, hogy az utolsó résztvevő elhagyta" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Nyilvánosan kereshető" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "A résztvevők megváltoztathatják a témát" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Jogosultság a Jabber-azonosítók megtekintéséhez" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kinek engedélyezett a résztvevők Jabber-azonosítóinak megtekintése?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Jelszó" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Jelszó a szobához való hozzáférés korlátozásához" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderált" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Csak a hanggal rendelkező résztvevők küldhetnek üzeneteket" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Csak tagok" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Csak tagok léphetnek be a szobába" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Üzenetelőzmények" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "A szoba üzenetelőzményeinek legnagyobb mennyisége" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Szoba beállítása" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Üdvözli a Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Jelentkezzen be, vagy hozzon létre egy fiókot az induláshoz." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Fiók beállítása" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Nincsenek aktív fiókok" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Fiókok kezelése" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Hibás jelszó" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Érvénytelen TLS-tanúsítvány" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Eltávolítja a(z) %s fiókot?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Eltávolítás" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Profilkép kiválasztása" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Képek" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Összes fájl" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Kapcsolódva" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Leválasztva" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Hiba" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Fiók hozzáadása" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "A kiszolgáló nem tudta bizonyítani, hogy ez %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Az operációs rendszer nem bízik a biztonsági tanúsítványában." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "A biztonsági tanúsítványát egy másik tartományra állították ki." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "A biztonsági tanúsítványa csak a jövőben válik érvényessé." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "A biztonsági tanúsítványa lejárt." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Bejelentkezés ide: %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Most már használhatja ezt a fiókot: %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Hibás felhasználónév vagy jelszó" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Valami tönkre ment" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Nincs válasz a kiszolgálótól" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Regisztráció ezen: %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "A kiszolgáló azt követeli, hogy regisztráljon egy weboldalon keresztül" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Weboldal megnyitása" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Regisztráció" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" "Nézze meg a %s oldalt azon információkért, hogy hogyan kell regisztrálni" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Üzenet szerkesztése" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ön" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Reakció hozzáadása" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Megnyitás" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Mentés másként…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s és %i partner éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s és %s éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s és %s éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s éppen ír…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Hívás elindítva" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Elindítva ennyi ideje: %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Ön ezt a hívást egy másik eszközön kezelte" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Hívás befejezve" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Befejezve ekkor: %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Eddig tartott: %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Nem fogadott hívás" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Ön nem fogadta ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s nem fogadta ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Hívás elutasítva" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Ön elutasította ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s elutasította ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Hívás sikertelen" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i óra" msgstr[1] "%i óra" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i perc" msgstr[1] "%i perc" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "néhány másodperc" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Ez a partner hozzá szeretné adni Önt a partnerlistájához" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Válasz" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "%s letöltése…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ezt ajánlotta: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Felajánlott fájl: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Felajánlott fájl" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "A fájlátvitel sikertelen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fájl" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Üzenet frissítése" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Önnek nincsenek nyitott csevegései" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" "Kattintson a + gombra egy csevegés indításához vagy egy csatornához való " "csatlakozáshoz" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Fiók" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Becenév" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Álnév" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Partner hozzáadása" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Értesítés új üzenet érkezésekor" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Hangulatjelek átalakítása emodzsikká" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Helyesírás-ellenőrzés" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Modern XMPP csevegőprogram" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra " "összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, " "miközben a magánszféra megőrzését is fontosnak tartja." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti " "titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint " "például az olvasási visszaigazolást és a gépelési értesítéseket." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az " "üzeneteket a többi eszközzel." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nincs aktív keresés" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Gépeljen a keresés elkezdéséhez" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nincsenek illeszkedő üzenetek" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ellenőrizze a helyesírást vagy próbálja meg eltávolítani a szűrőket" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Küldés" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Általános" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Beszélgetés" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigáció" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ugrás a következő beszélgetésre" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ugrás az előző beszélgetésre" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Fiókok" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Helyi álnév" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nincsenek beállított fiókok" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Fiók hozzáadása" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Bejelentkezés" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Fiók létrehozása" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Nem sikerült kiépíteni biztonságos kapcsolatot" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Kapcsolódás" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Nyilvános kiszolgáló kiválasztása" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Vagy egy kiszolgáló címének megadása" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Bejelentkezés inkább" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Másik kiszolgáló választása" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Minden készen áll!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Befejezés" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Kattintson ide egy beszélgetés indításához vagy egy csatornához való " #~ "csatlakozáshoz." #~ msgid "No active conversations" #~ msgstr "Nincs aktív beszélgetés" #~ msgid "Main window with conversations" #~ msgstr "A fő ablak a beszélgetésekkel" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s és %i további felhasználó" #~ msgid "Save" #~ msgstr "Mentés" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s, és %s" #~ msgid "%s and %s" #~ msgstr "%s és %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "gépel…" #~ msgstr[1] "gépel…" #~ msgid "has stopped typing" #~ msgstr "abbahagyta a gépelést" #~ msgid "Discover real JIDs" #~ msgstr "Valódi JID-k megtekintése" #~ msgid "Who may discover real JIDs?" #~ msgstr "Ki láthatja a valódi JID-ket?" #~ msgid "Password required for room entry, if any" #~ msgstr "A szobába lépéshez szükséges jelszó, ha van" #~ msgid "Join Conference" #~ msgstr "Csatlakozás konferenciához" #~ msgid "Preferences" #~ msgstr "Beállítások" #~ msgid "Quit" #~ msgstr "Kilépés" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "" #~ "A JID-nek ebben a formátumban kell lennie: \"felhasználó@szolgáltató.com\"" #~ msgid "Copy Link Address" #~ msgstr "Link címének másolása" #~ msgid "Copy" #~ msgstr "Másolás" #~ msgid "Select All" #~ msgstr "Összes kijelölése" #~ msgid "Search" #~ msgstr "Keresés" dino-0.4.3/main/po/id.po0000644000000000000000000010562714452563620013465 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-01-04 01:53+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.10.1\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Ukuran file melebihi ukuran unggahan maksimum." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Pesan terlalu panjang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "Diedit" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "tertunda…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "pengiriman gagal" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Tidak terenkripsi" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Tidak bisa mengirimkan pesan" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i menit yang lalu" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Sesaat lalu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Saya" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Gambar terkirim" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File terkirim" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Gambar diterima" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "File diterima" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Panggilan keluar" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Panggilan masuk" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Kemarin" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Mulai percakapan" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Pemilik" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Moderator" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Anggota" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Pengguna" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Undang" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Mulai percakapan pribadi" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Depak" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Beri ijin menulis" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Batalkan ijin menulis" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Undang ke Grup" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Pilih file" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Pilih" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Batal" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s dari %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Grup ini tidak mengijinkan anda mengirim pesan." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Meminta ijin" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Kirim file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Mulai panggilan" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Panggilan audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Panggilan video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Cari pesan" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anggota" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Memanggil…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Berdering…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Menghubungi…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s mengakhiri panggilan" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s menolak panggilan" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Mulai" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Bergabung dengan Channel" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Selanjutnya" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Bergabung" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Kembali" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bergabung…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Password diperlukan untuk memasuki kamar" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Dilarang bergabung atau membuat grup" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Kamar tidak ada" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Tidak diperbolehkan membuat kamar" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Kamar khusus anggota" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Pilih nama panggilan lain" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Terlalu banyak penghuni di kamar" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Tidak terhubung ke %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Alamat tidak valid" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Tambah" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Kombinasi tombol" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Tentang Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hari ini" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i hasil pencarian" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "di %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Pada %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Panggilan video masuk" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Tolak" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Terima" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Meminta ijin" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Tolak" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Undangan untuk %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s mengundang anda ke %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Permintaan ijin" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%smeminta ijin untuk menulis ke dalam %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Pengaturan" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Pengaturan lokal" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Kirim pemberitahuan pengetikan" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Kirim tanda telah dibaca" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notifikasi" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "On" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Mati" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Hanya jika dipanggil" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Pengaturan awal: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Permintaan" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Ijin" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Meminta ijin untuk mengirim pesan" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detail Grup" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detail kontak" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blokir" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Komunikasi dan pembaruan status diblokir pada kedua arah" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nama kamar" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Deskripsi kamar" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Abadi" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Kamar tetap ada setelah penghuni terakhir pergi" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Dapat ditemukan" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Penghuni dapat mengubah subyek pembicaraan" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Ijin untuk melihat JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Siapa yang diijinkan untuk melihat JID penghuni?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Kata sandi" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Kata sandi untuk membatasi akses ke kamar" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Dimoderasi" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Hanya penghuni dengan akses suara yang boleh mengirim pesan" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Khusus anggota" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Hanya anggota yang boleh memasuki kamar" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Riwayat pesan" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Jumlah maksimum backlog yang diterbitkan oleh kamar" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Konfigurasi kamar" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Selamat datang di Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Masuk atau buat akun untuk memulai." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Buat akun" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Tidak ada akun aktif" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Atur akun" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Password salah" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Sertifikat TLS tidak valid" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Hapus akun %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Hapus" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Pilih gambar profil" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Gambar" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Semua file" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Terhubung" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Terputus" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Rusak" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Tambah akun" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Server tidak dapat membuktikan bahwa ini adalah %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Sertifikat keamanannya tidak dipercaya oleh OS Anda." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Sertifikat keamanan dikeluarkan untuk domain lain." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Sertifikat keamanan hanya akan berlaku di masa mendatang." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Sertifikat keamanan tidak berlaku." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Masuk ke %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Akun dapat digunakan %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nama pengguna atau kata sandi salah" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Ada yang tidak beres" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Server tidak merespon" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Terdaftar pada %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Server ini mengharuskan pendaftaran melalui website" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Buka website" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Daftar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Periksa %s untuk informasi pendaftaran" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s, dan %i lainnya sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, dan %s sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s dan%s sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s sedang mengetik…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Panggilan dimulai" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Dimulai %s yang lalu" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Anda menangani panggilan ini di perangkat lain" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Panggilan berakhir" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Berakhir pada %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Berlangsung %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Panggilan tak terjawab" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Anda melewatkan panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s melewatkan panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Panggilan ditolak" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Anda menolak panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s menolak panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Panggilan gagal" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i jam" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i menit" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "beberapa detik" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Kontak ini ingin menambahkan anda kedalam daftar kontaknya" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Mengunduh %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ditawarkan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "File ditawarkan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "File ditawarkan" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transfer file gagal" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Update pesan" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Anda tidak memiliki percakapan terbuka" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Akun" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Panggilan" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Tambah kontak" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Beri tahu saat ada pesan" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Ubah smiley menjadi emoji" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Periksa ejaan" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Aplikasi chat XMPP modern" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino adalah aplikasi chat open source modern untuk PC. Menyediakan " "pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan " "pengaturan fitur terkait privasi seperti tanda pesan dibaca dan " "pemberitahuan pengetikan." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan " "perangkat lain." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Tidak ada pencarian aktif" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Ketik untuk memulai pencarian" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Tidak ada pesan yang cocok" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Periksa ejaan atau coba hapus filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Kirim" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Umum" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Percakapan" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigasi" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Pesan selanjutnya" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Pesan sebelumnya" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Akun" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias lokal" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Tidak ada akun yang dikonfigurasi" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Tambah akun" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Masuk" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Buat akun" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Tidak dapat membuat sambungan aman" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Hubungi" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Pilih server publik" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Atau tentukan alamat server" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Masuk saja" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Pilih server lain" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Selesai!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Selesai" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klik untuk memulai percakapan atau bergabung dengan channel." dino-0.4.3/main/po/ie.po0000644000000000000000000010727114452563620013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-01 10:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Li grandore de file excede li maximum del servitor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Li missage es tre long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "redactet" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ínciffrat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Ne successat inviar li missage" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%e %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%e %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "ante %i min" msgstr[1] "ante %i mins" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Strax" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Yo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Image sta inviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File sta inviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Image sta recivet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Image es recivet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%e %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Yer" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Iniciar un conversation" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietario" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usator" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invitar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar un privat conversation" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Remover" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Dar li permission scrir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar li permission scrir" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitar a un conferentie" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Selecter un file" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Selecter" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anullar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ti-ci conferentie ne permisse a vos inviar missages." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demandar li permission" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Inviar un file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Serchar missages" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conexion…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Adherer al chanele" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Avan" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Adherer" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Retro" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Adherente…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Un contrasigne es besonat por intrar li chambre" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Prohibit de adhesion o creation de conferenties" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Chambre ne existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ne es permisset crear chambres" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Solmen por membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Selecte un different nómine" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tro mult occupantes in li chambre" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Ne successat conexer a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Adresse es ínvalid" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adjunter" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Rapid-tastes" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Pri Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hodie" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultate de sercha" msgstr[1] "%i resultates de sercha" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Refusar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Petition de abonnament" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Refusar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitation a(l) %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s invitat vos a(l) %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Demande de permission" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demanda li permission scrir in %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Parametres" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Local parametres" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Inviar notificationes pri li tippada" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Inviar confirmationes de letion" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificationes" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Yes" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "No" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Solmen quande es mentionat" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Predefinit: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Demandar" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permissiones" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Demandar li permission inviar missages" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detallies del conferentie" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detallies del contacte" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blocar" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Actualisation del communication e statu es blocat in ambi directiones" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nómine del chambre" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrition del chambre" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistent" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Li chambre va persister pos que li ultim occupant surti" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Serchabil publicmen" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Occupantes posse cambiar li tema" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permission vider JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui es permisset vider li JIDs del membres?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Contrasigne" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Un contrasigne por restricter li accesse al chambre" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderat" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Solmen occupantes con voce posse inviar missages" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Solmen membres" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Solmen li membres posse intrar li chambre" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Diarium de missages" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Max númere de old missages gardat per li chambre" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuration del chambre" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Benevenit a Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Inregistra vos o crea un conto por iniciar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Etablisser un conto" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Null activ contos" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gerer contos" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Contrasigne es ínvalid" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificate TLS es ínvalid" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Remover li conto %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Remover" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Selecte un avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Images" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Omni files" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conexet" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Disconexet" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Errore" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Adjunter un conto" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Li servitor ne posset provar que it es %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Su certificat ne have fide de vor sistema operativ." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Su certificat es emisset por un altri dominia." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Su certificat va esser valid solmen in li futur." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Su certificat ha expirat." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Inregistrar se in %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Nu vu posse usar li conto %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Contrasigne o nómine ínvalid" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Un errore evenit" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Servitore ne responde" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrar sur %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Li servitor besona r" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Aperter li website" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Visita %s por li information pri registration" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i altri tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s tippa…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Ti-ci contacte desira adjunter vos al su contact-liste" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Descargante %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ha ofertat: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "File sta ofertat: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Un file sta ofertat" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transferte de un file ne successat" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualisar li missage" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vu ne have apertet conversationes" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nómine" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonim" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adjunter li contacte" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notificar quande un missage ariva" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Converter smileys a emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Un modern client de conversationes XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un modern cliente de conversationes con fonte apert. It foca se sur " "provider un nett e fidibil experientie de Jabber/XMPP con un attention a " "confidentialitá." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar " "sensitiv functiones quam confirmation de lectada e notificationes de tippada." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtene li diarium del servitore e sincronisa missages inter altri " "apparates." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Null activ sercha" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tippa por iniciar un sercha" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Null correspondent missages" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controla li ortografie o remove filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Inviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "General" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversation" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigation" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ear al sequent conversation" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ear al precedent conversation" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Contos" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Local pseudonim" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Null contos etablisset" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Adjunter un conto" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Inregistrar se" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crear un conto" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Ne successat etablisser un secur conexion" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connexer" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Selecte un public servitore" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "O provide un adresse de servitore" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "O inregistrar se" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Selecte un altri servitore" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Omni es pret!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finir" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Fa un clic ti-ci por iniciar un conversation o adherer a un channel." #~ msgid "No active conversations" #~ msgstr "Null activ conversationes" #~ msgid "Main window with conversations" #~ msgstr "Li primari fenestre con conversationes" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i altri" #~ msgid "You can now start using %s" #~ msgstr "Nu vu posse usar %s" #~ msgid "Open Registration" #~ msgstr "Apert registration" #~ msgid "Save" #~ msgstr "Gardar quam" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippa…" #~ msgstr[1] "tippa…" #~ msgid "has stopped typing" #~ msgstr "ne tippa plu" #~ msgid "%i search results" #~ msgstr "resultates: %i" #~ msgid "Discover real JIDs" #~ msgstr "Decovrir real JIDs" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui posse decovrir real JIDs?" #~ msgid "Password required for room entry, if any" #~ msgstr "Un contrasigne por intrar li chambre" dino-0.4.3/main/po/is.po0000644000000000000000000010725514452563620013503 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-08-23 18:21+0000\n" "Language-Team: none\n" "Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n" "X-Generator: Weblate 4.14-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Skráin fer yfir hámarksupphleðslustærð þjónsins." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Skilaboð of langt" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "breytt" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "að senda…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "afhending mistókst" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ódulkóðað" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Ekki er hægt að senda skilaboð" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Fyrir %i mín" msgstr[1] "Fyrir %i mín" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Núna" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ég" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Mynd send" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Skrá send" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Mynd móttekin" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Skrá móttekin" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Símtal í útleið" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Símtal í innleið" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Í gær" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Byrja samtal" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigandi" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Stjórnandi" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Meðlimur" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Notandi" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Bjóða" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Byrja einkasamtal" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Reka út" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Veita skrifleyfi" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Afturkalla skrifleyfi" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Bjóða í spjall" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Veldu skrá" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Velja" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Hætta við" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s frá %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Þér hefur ekki verið leyft að senda skilaboð í þessu samtali." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Óska eftir leyfi" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Senda skrá" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hefja símtal" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hljóðsímtal" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Myndsímtal" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Leita að skilaboðum" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Meðlimar" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Villuleitarupplýsingar" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Hringir…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Hringir…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Tengist…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s lauk símtali" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s afþakkaði símtalið" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Myndavélar" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Myndavél fannst ekki." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Hljóðnemar" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Hljóðnemi fannst ekki." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hátalarar" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Hátalari fannst ekki." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Bjóða í símtal" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Byrja" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Tengjast rás" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Áfram" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Tengjast" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Til baka" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Tengist…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lykilorð þarf til að komast inn í herbergi" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Bannað að taka þátt í eða skapa ráðstefnu" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Herbergið er ekki til" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Óheimilt að búa til herbergi" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Herbergi er eingöngu fyrir meðlima" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Veldu annað gælunafn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Það eru of margir í herberginu" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Gat ekki tengst %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ógilt netfang" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Bæta við" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Flýtilyklar" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Um Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Í dag" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i leitarniðurstaða" msgstr[1] "%i leitarniðurstöður" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Í %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Með %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Óskast eftir myndsímtali" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Óskast eftir hópmyndsímtali" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Óskast eftir hópsímtali" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Hafna" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Samþykkja" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Áskriftarbeiðni" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Hafna" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Boð í %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s bauð þér í %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Beiðni um leyfi" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s biður um leyfi til að skrifa í %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Stillingar" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Staðbundnar stillingar" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Senda innsláttartilkynningar" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Senda lestrarkvittanir" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Tilkynningar" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Virkt" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Óvirkt" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Aðeins þegar minnst er á mig" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Sjálfgefið: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Biðja um" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Heimildir" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Biðja um leyfi til að senda skilaboð" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Upplýsingar um ráðstefnu" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Tengiliðaupplýsingar" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Loka á" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Lokað er fyrir samskipta- og stöðuuppfærslur í báðar áttir" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Heiti herbergis" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Lýsing á herbergi" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Bjarga tómu herbergi" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" "Herbergið verður áfram til jafnvel eftir að síðasti notandinn er farinn" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Hægt er að leita eftir fyrir almenning" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Gestir geta breytt umræðuefni spjallsins" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Leyfi til að skoða JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Hverjum er heimilt að skoða JID gesta?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Lykilorð" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Lykilorð til að takmarka aðgang að herbergi" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Stýrt" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Aðeins gestir með rödd (heimild) geta sent skilaboð" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Aðeins meðlimar" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Aðeins meðlimir mega fara inn í herbergi" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Skilaboðaferill" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Hámarksfjöldi sögu sem herbergið gefur út" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Herbergisstilling" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Velkomin(n) í Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Skráðu þig inn eða búðu til reikning til að byrja." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Stofna reikning" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Engir virkir reikningar" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Stjórna reikningum" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Rangt lykilorð" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Ógilt TLS vottorð" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Fjarlægja reikning %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Fjarlægja" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Velja notandamynd" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Myndir" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Allar skrár" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Tengdur" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Ótengdur" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Villa" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Bæta við reikningi" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Þjónninn gat ekki sannað eign léns %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Öryggisvottorð hans er ekki treyst af stýrikerfinu þínu." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Öryggisvottorð hans er gefið út á annað lén." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Öryggisvottorð hans mun aðeins gilda í framtíðinni." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Öryggisvottorð hans er útrunnið." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Skrá inn á %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Þú getur nú notað reikninginn %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Rangt notandanafn eða lykilorð" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Eitthvað fór úrskeiðis" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Ekkert svar frá þjóni" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Nýskrá á %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Þjónninn þarfnast að þú skráir þig í gegnum vefsíðu" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Opna vefsíðu" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Nýskrá" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Athugaðu %s til að fá upplýsingar um hvernig á að nýskrá" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Opna" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Vista sem…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s og %i aðrir eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s og %s eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s og %s eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s er að skrifa…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Símtal hafið" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Byrjaði fyrir %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Þú svaraðir þessu símtali í öðru tæki" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Símtali lokið" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Laukst %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Entist %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Ósvarað símtal" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Þú misstir af þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s missti af þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Símtali hafnað" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Þú hafnaðir þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s afþakkaði þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Símtal mistókst" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i klst" msgstr[1] "%i klst" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i mín" msgstr[1] "%i mín" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "nokk(rum/rar) sekúndu(m/r)" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Þessi tengiliður vill bæta þér við tengiliðalistann sinn" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Að sækja %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s. Stærð: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Skráarstærð: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Skrá í boði" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Skráaflutningur mistókst" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Breyta skilaboði" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Þú hefur engin opin spjöll" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Reikningur" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Gælunafn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Samnefni" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Bæta við" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Láta vita af nýjum skilaboðum" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Breytta :) í 🙂 sjálfvirkt" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Kanna stafsetningu" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Nútímalegt XMPP-spjallforrit" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino er nútímalegt spjallforrit og frjáls hugbúnaður fyrir skjáborðið. Það " "leggur áherslu á að veita hreina og áreiðanlega Jabber/XMPP upplifun með " "friðhelgi þína í huga." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Það styður dulkóðun frá enda til enda með OMEMO og OpenPGP og gerir kleift " "að stilla persónuverndartengda eiginleika eins og lestrarkvittanir og " "innsláttartilkynningar." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino sækir feril af netþjóni og samstillir skilaboð með öðrum tækjum." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Engin virk leit" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skrifaðu til að hefja leit" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Engin samsvarandi skilaboð" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Athugaðu stafsetninguna eða reyndu að fjarlægja síur" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Senda" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Almennt" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Samtal" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Hreyfing" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Fara í næsta spjall" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Fara í fyrra spjall" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Reikningar" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Staðbundið samnefni" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Engir reikningar stilltir" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Bæta við reikningi" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Skrá inn" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Búa til reikning" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Mistókst að skapa örugga tengingu" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Tengjast" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Velja opinberan netþjón" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Eða tilgreina lén netþjóns" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Skrá inn í staðinn" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Velja annan netþjón" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Allt klárt!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Loka" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Smelltu hér til að hefja samtal eða taka þátt í rás." dino-0.4.3/main/po/it.po0000644000000000000000000011416014452563620013475 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Il file eccede la dimensione massima di upload." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Messaggio troppo lungo" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "modificato" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "in attesa…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "invio fallito" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Non cifrato" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Impossibile inviare il messaggio" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuto fa" msgstr[1] "%i minuti fa" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Adesso" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Io" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Immagine inviata" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File inviato" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Immagine ricevuta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "File ricevuto" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Chiamata in uscita" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Chiamata in arrivo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ieri" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Inizia una conversazione" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietario" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Amministratore" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utente" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invita" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Inizia una conversazione privata" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Espelli" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Concedi il permesso di scrivere" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca il permesso di scrivere" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invita alla conferenza" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Seleziona un file" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Seleziona" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annulla" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s da %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Questa conferenza non ti permette di inviare messaggi." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Richiesta di autorizzazione" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Invia un file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Chiama" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chiamata audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videochiamata" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Cerca tra i messaggi" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Partecipanti" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informazioni di debug" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Chiamando…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Sta squillando…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Connessione…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s ha terminato la chiamata" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s ha rifiutato la chiamata" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Telecamere" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nessuna telecamera trovata." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoni" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nessun microfono trovato." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altoparlanti" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nessun altoparlante trovato." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invita alla chiamata" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Inizia" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Entra nel canale" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Avanti" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Partecipa" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Indietro" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Accesso in corso…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Una password è richiesta per entrare nella stanza" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Ti è proibito entrare o creare una conferenza" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La stanza non esiste" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Non sei abilitato a creare la stanza" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "La stanza è solo per membri" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Scegli un soprannome differente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "La stanza ha troppi partecipanti" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Impossibile connettersi a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Indirizzo non valido" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Aggiungi" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Scorciatoie da tastiera" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Informazioni su Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Oggi" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i risultato per la ricerca" msgstr[1] "%i risultati per la ricerca" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Videochiamata in arrivo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Videochiamata di gruppo in arrivo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Chiamata di gruppo in arrivo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rifiuta" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accetta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Richiesta di iscrizione" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Rifiuta" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invito per %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s ti ha invitato a %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Richiesta di autorizzazione" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ha chiesto il permesso di scrivere in %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Impostazioni" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Impostazioni locali" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Invia notifiche di digitazione" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Invia ricevute di lettura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notifiche" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Attivo" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Disattivo" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Solo se menzionato" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Predefinito: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Richiedi" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permessi" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Richiedi il permesso di inviare messaggi" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Dettagli della conferenza" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Dettagli del contatto" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blocca" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "La comunicazione e gli aggiornamenti di stato sono bloccati in entrambe le " "direzioni" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nome della stanza" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrizione della stanza" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistente" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "La stanza continuerà ad esistere dopo l'uscita dell'ultimo occupante" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Ricercabile pubblicamente" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "I partecipanti possono cambiare l'argomento" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Autorizzazione per visualizzare i JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Chi è autorizzato a vedere i JID degli occupanti?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Password" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Una password per limitare l’accesso al gruppo" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderata" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Solo i partecipanti autorizzati possono mandare messaggi" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Solo per membri" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Solo i membri possono entrare nella stanza" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Cronologia dei messaggi" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Numero massimo di messaggi in cronologia restituiti dalla stanza" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configurazione della stanza" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Benvenuto in Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Accedi o crea un account per iniziare." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configura account" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Nessun account attivo" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gestisci account" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Password sbagliata" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Il certificato TLS non è valido" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Rimuovere l'account %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Rimuovi" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Scegli una foto profilo" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Immagini" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Tutti i documenti" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Connesso" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Disconnesso" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Errore" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Aggiungi un account" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Il server non può dimostrare di essere %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Il certificato di sicurezza non è riconosciuto dal tuo sistema." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Il certificato di sicurezza è rilasciato per un altro dominio." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Il certificato di sicurezza diventerà valido solo in futuro." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Il certificato di sicurezza è scaduto." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Accedi a %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Ora è possibile iniziare a usare l’account %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nome utente o password sbagliati" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Qualcosa è andato storto" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Nessuna risposta dal server" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrati su %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Il server richiede di registrarsi tramite un sito web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Apri il sito web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrati" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Controlla %s per informazioni su come registrarsi" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Apri" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salva come…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i altri stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s sta scrivendo…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Chiamata iniziata" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Iniziata %s fa" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Hai gestito questa chiamata su un altro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Chiamata terminata" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Terminata alle %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Durata %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Chiamata persa" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Hai perso questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s ha perso questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Chiamata rifiutata" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Hai rifiutato questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s ha rifiutato questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Chiamata non riuscita" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ora" msgstr[1] "%i ore" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minuti" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "qualche secondo" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Questo contatto vorrebbe aggiungerti alla sua lista contatti" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Scaricamento di %s in corso…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ha offerto: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "File offerto: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "File offerto" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Trasferimento del file non riuscito" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "File" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aggiorna il messaggio" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Non hai chat aperte" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Account" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Soprannome" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Aggiungi contatto" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notifica quando arriva un nuovo messaggio" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Trasforma le faccine in emoji" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Controlla l'ortografia" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Client di chat moderno per XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino è un client di chat per il desktop, moderno e open-source. Si concentra " "nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente " "la tua privacy." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di " "configurare le funzioni relative alla privacy come le ricevute di lettura e " "le notifiche di digitazione." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera la cronologia dal server e sincronizza i messaggi con gli " "altri dispositivi." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nessuna ricerca attiva" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Digita per iniziare una ricerca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nessun messaggio corrispondente" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controlla l'ortografia o prova a rimuovere dei filtri" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Invia" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Generali" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversazione" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigazione" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Passa alla conversazione successiva" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Passa alla conversazione precedente" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Account" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias locale" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nessun account configurato" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Aggiungi un account" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Accedi" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crea account" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Impossibile stabilire una connessione sicura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connetti" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Scegli un server pubblico" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Oppure inserisci l'indirizzo di un server" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Altrimenti accedi" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Scegli un altro server" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tutto pronto!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Fine" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Fai clic qui per iniziare una conversazione o per entrare in un canale." #~ msgid "No active conversations" #~ msgstr "Nessuna conversazione attiva" #~ msgid "Main window with conversations" #~ msgstr "La finestra principale con le conversazioni" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i altri" #~ msgid "You can now start using %s" #~ msgstr "Adesso puoi iniziare ad usare %s" #~ msgid "Open Registration" #~ msgstr "Registrazioni aperte" #~ msgid "Save" #~ msgstr "Salva" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s, e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "sta scrivendo…" #~ msgstr[1] "stanno scrivendo…" #~ msgid "has stopped typing" #~ msgstr "ha smesso di scrivere" #~ msgid "%i search results" #~ msgstr "%i risultati per la ricerca" #~ msgid "Discover real JIDs" #~ msgstr "Scopri i veri JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Chi può scoprire i veri JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "Password richiesta per entrare nella stanza, se impostata" #~ msgid "Failed connecting to %s" #~ msgstr "Connessione a %s fallita" #~ msgid "Join Conference" #~ msgstr "Partecipa alla conferenza" #~ msgid "Communicate happiness." #~ msgstr "Comunica felicità." #~ msgid "Preferences" #~ msgstr "Preferenze" #~ msgid "Quit" #~ msgstr "Esci" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Il JID dovrebbe essere nella forma \"utente@example.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copia l'indirizzo del collegamento" #~ msgid "Copy" #~ msgstr "Copia" #~ msgid "Select All" #~ msgstr "Seleziona Tutto" #~ msgid "Search" #~ msgstr "Cerca" #~ msgid "Send message marker" #~ msgstr "Invia lo stato del messaggio" #~ msgid "Start Chat" #~ msgstr "Inizia una conversazione" #~ msgid "Request presence updates" #~ msgstr "Richiedi gli aggiornamenti di stato" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Questa è una notifica dell'app. Clicca il bottone per chiudere" #~ msgid "Join on startup" #~ msgstr "Entra all'avvio" #~ msgid "Add Chat" #~ msgstr "Aggiungi conversazione" dino-0.4.3/main/po/ja.po0000644000000000000000000011575614452563620013467 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-09 01:59+0000\n" "Language-Team: Japanese \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "ファイルサイズがサーバーの最大アップロード可能サイズを超過しています。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "メッセージが長すぎます" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "編集済み" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "発信中…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "配信に失敗しました" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "非暗号化" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "メッセージを送信できません" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b%d日、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b%d日、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a曜日、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a曜日、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "たった今" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "自分" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "画像を送信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "ファイルを送信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "画像を受信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "ファイルを受信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "発信呼び出し" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "着信" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b%d日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨日" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "トークを開始" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "オーナー" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理人" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "メンバー" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "ユーザー" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "招待" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "個人チャットを始める" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "退出させる" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "送信権限を許可" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "送信権限を取り消す" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "トークルームへ招待" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "ファイルを選択" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "選択" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "キャンセル" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%2$s の %1$s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "このトークルームでは、メッセージの送信が許可されていません。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "送信権限を要求" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "ファイルを送信" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "通話開始" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "音声通話" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "ビデオ通話" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "メッセージを検索" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "メンバー" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "デバッグ情報" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "呼び出し中…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "鳴っている…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "接続試行中…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%sは通話を終了しました" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s は通話を拒否しました" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "開始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "チャンネルに参加" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "次へ" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "参加" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "戻る" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "参加試行中…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "トークルームに参加するにはパスワードが必要です" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "参加中または作成中のトークルームから退会させられました" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "トークルームは存在しません" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "トークルームを作成する権限がありません" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "メンバー制トークルーム" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "別のニックネームを選んでください" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "トークルームの参加者が多すぎます" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "%s に接続できませんでした" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "アドレスが不正です" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "追加" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "キーボードショートカット" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Dino について" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今日" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%b%d日 (%a)" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 件の検索結果" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "%s での検索結果" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "%s との会話での検索結果" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "着信ビデオ通話" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "拒否" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "承諾" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "在席通知の申込" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "拒否" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "%s への招待" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s さんがあなたを %s に招待しました" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "送信権限の要求" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s さんが %s での送信権限を要求しています" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "設定" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "ローカル設定" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "入力中であることを通知" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "既読状態を送信" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "通知" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "オン" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "オフ" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "メンションされた場合のみ" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "デフォルト: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "要求" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "送信権限" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "メッセージの送信権限を要求" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "トークルームの詳細" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "連絡先の詳細" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "ブロック" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "双方のコミュニケーションと状態の更新がブロックされます" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "トークルームの名前" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "トークルームの説明" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "トークルームを自動削除しない" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "最後の参加者が退会してもトークルームを残します" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "外部から検索可能にする" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "参加者にタイトルの変更を許可する" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "JID を表示できるメンバー" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "参加者の JID を表示できるメンバーを選択します" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "パスワード" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "トークルームへのアクセスを制限するためのパスワード" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "発言制限" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "発言権のある参加者のみがメッセージを送れます" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "メンバー制" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "メンバーのみが入室できます" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "メッセージの履歴" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "保存するトーク履歴の数" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "トークルームの設定" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Dino へようこそ!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "サインインまたはアカウント登録をしてください。" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "アカウントをセットアップ" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "アクティブなアカウントがありません" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "アカウントを管理" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "パスワードが違います" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "TLS 証明書が不正です" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "アカウント %s を削除しますか?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "削除" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "アバターを選択" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "画像" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "すべてのファイル" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "接続済み" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "切断" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "エラー" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "アカウントを追加" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "サーバーはこれが %s だと証明できませんでした。" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "このセキュリティー証明書はオペレーティングシステムに信頼されていません。" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "このセキュリティー証明書はほかのドメインのために発行されています。" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "このセキュリティー証明書が有効になる日時になっていません。" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "このセキュリティー証明書は有効期限切れです。" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "%s にサインイン" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "アカウント %s を使用できるようになりました。" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "ユーザー名またはパスワードが違います" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "問題が発生しました" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "サーバーから応答がありません" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "%s に登録" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "サーバーが Web サイトでのサインアップを要求しています" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Web サイトを開く" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "登録" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "サインアップの方法に関する情報は、%s をご確認ください" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s さん、%s さんに加えて、%i 人のメンバーが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s さん、%s さん、%s さんが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s さんと %s さんが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s さんが入力しています…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "通話開始" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "%s 前に始まりました" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "別のデバイスでこの呼び出しを処理しました" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "通話終了" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "%s で終了" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "%s 継続していた" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "不在着信" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "この電話に出られなかった" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s はこの呼び出しに出られなかった" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "通話が拒否されました" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "通話を拒否しました" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s はこの呼び出しを拒否しました" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "呼び出しに失敗しました" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i 時間" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i 分" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "この相手があなたを連絡先に入れようとしています" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "%s をダウンロードしています…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s を受信しました: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "ファイルを受信しました: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "ファイルが提供されています" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "ファイルの転送に失敗しました" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "ファイル" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "メッセージを更新" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "表示するトークはまだありません" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "アカウント" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "ニックネーム" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "別名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "連絡先を追加" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "新しいメッセージが届いたときに通知" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "スマイリーを絵文字に変換" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "スペルチェック" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "現代的な XMPP チャット クライアント" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プラ" "イバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供" "を第一に考えて開発されています。" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送" "信や入力通知などのプライバシー関連の設定も可能です。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "まだ検索していません" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "入力して検索を開始してください" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "一致するメッセージはありません" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "スペルを確認するか、フィルターを消去してみてください" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "送信" #: main/data/shortcuts.ui:10 msgid "General" msgstr "一般" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "トーク" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "ナビゲーション" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "次のトークへ移動" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "前のトークへ移動" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "アカウント" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "ローカルでの別名" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "アカウントが設定されていません" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "アカウントを追加" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "サインイン" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "アカウントを作成" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "安全な接続を確立できませんでした" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "接続" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "公開サーバーを選択" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "または、サーバーのアドレスを手入力" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "代わりにサインイン" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "別のサーバーを選択" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "すべてのセットアップが完了しました!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "完了" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "トークを始めたりトークルームに参加したりするには、ここをクリックしてくださ" #~ "い。" #~ msgid "No active conversations" #~ msgstr "アクティブなトークがありません" #~ msgid "Main window with conversations" #~ msgstr "トーク中のメインウィンドウ" #~ msgid "%s, %s and %i others" #~ msgstr "%s さん、%s さんとその他 %i 人" #~ msgid "You can now start using %s" #~ msgstr "%s をお使いいただけます" #~ msgid "Open Registration" #~ msgstr "登録を開く" #~ msgid "Save" #~ msgstr "保存" #~ msgid "%s, %s and %s" #~ msgstr "%s さん、%s さんと %s さん" #~ msgid "%s and %s" #~ msgstr "%s さんと %s さん" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "が入力しています…" #~ msgid "has stopped typing" #~ msgstr "が入力を中断しました" #~ msgid "%i search results" #~ msgstr "%i 件の検索結果" #~ msgid "Discover real JIDs" #~ msgstr "JID の検索" #~ msgid "Who may discover real JIDs?" #~ msgstr "JID の検索を許可する対象" #~ msgid "Password required for room entry, if any" #~ msgstr "トークルームへの参加に必要なパスワード" #~ msgid "Failed connecting to %s" #~ msgstr "%s への接続に失敗しました" #~ msgid "Join Conference" #~ msgstr "トークルームに参加" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID は “user@example.com” の形式にしてください" #~ msgid "Copy Link Address" #~ msgstr "リンクアドレスをコピー" #~ msgid "Copy" #~ msgstr "コピー" #~ msgid "Select All" #~ msgstr "すべて選択" #~ msgid "Search" #~ msgstr "検索" #~ msgid "Start Chat" #~ msgstr "会話を開始" dino-0.4.3/main/po/kab.po0000644000000000000000000007543514452563620013631 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: none\n" "Language: kab\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Yetteqqen…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Awal n uɛeddi" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Awal n uɛeddi d armeɣtu" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Yeqqen" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Isenser" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Isem n uɛeddi neɣ isem n useqdac d arameɣtu" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Tuqqna" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/ko.po0000644000000000000000000010762414452563620013501 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-10-17 00:56+0000\n" "Language-Team: none\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "파일이 서버 허용 최대 용량을 초과하였습니다." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "메세지가 너무 김" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "수정됨" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "대기중…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "전송 실패" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "비암호화" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "메세지를 보낼 수 없음" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 분 이전에" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "방금 전" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "나" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "이미지 전송 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "파일 전송 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "이미지 수신 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "파일 수신 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "전화 송신 중" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "전화 수신 중" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "어제" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "대화 시작" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "소유자" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "관리자" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "구성원" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "사용자" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "초대" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "사적 대화 시작하기" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "추방" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "쓰기 권한 부여" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "쓰기 권한 회수" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "대화 그룹에 초대" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "파일 선택" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "선택" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "취소" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 #, fuzzy msgid "This conference does not allow you to send messages." msgstr "이 대화 그룹에서의 메세지 전송이 허가되지 않았습니다." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "권한 요청" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "파일 전송" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "전화 걸기" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "음성 통화" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "영상 통화" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "메세지 검색" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "구성원들" #: main/src/ui/call_window/participant_widget.vala:118 #, fuzzy msgid "Debug information" msgstr "정보 디버그" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "전화 거는 중…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "전화 수신 중…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "연결 중…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s 가 통화를 종료함" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s 가 통화를 거부함" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "카메라" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "카메라를 찾지 못했습니다." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "마이크" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "마이크를 찾지 못했습니다." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "스피커" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "스피커를 찾지 못했습니다." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "통화에 초대" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "시작" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "채널 참가" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "다음" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "참가" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "이전" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "참가 중…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "방에 참가하기 위해 비밀번호가 필요합니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "참여하거나 만든 그룹 대화에서 추방됐습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "방이 존재하지 않습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "방 생성이 허용되지 않았습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "회원 전용 방" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "다른 닉네임을 고르세요" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "방에 사용자가 너무 많습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "%s에 연결하지 못했습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "잘못된 주소" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "추가" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "키보드 단축키" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Dino에 대하여" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "오늘" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 개의 검색 결과" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "%s와 함께" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "영상 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "그룹 영상 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "그룹 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "거부" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "허용" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "친구 추가 요청" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "거부" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "%s로 초대됨" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s가 %s로 초대함" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "권한 요구" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s가 %s에 쓰기 권한을 요청함" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "설정" #: main/src/ui/contact_details/settings_provider.vala:13 #, fuzzy msgid "Local Settings" msgstr "일부 설정" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "입력 중 알림 전송" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "읽음 알림 전송" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "알림" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "켜기" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "끄기" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "언급 되었을 때만" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "기본값: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "요청" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "권한" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "메세지 전송을 위한 권한 요청" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "대화 그룹 세부 사항" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "연락처 세부 사항" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "차단" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "양방향에서의 소통과 상태 갱신이 차단됩니다" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "방의 이름" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "방의 설명" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 #, fuzzy msgid "Persistent" msgstr "유지성" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "방이 마지막 구성원이 나가도 유지됩니다" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "공개적으로 검색 가능" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "참여자가 주제를 바꿈" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "JID를 볼 권한" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "누가 참여자들의 JID를 볼 수 있게 허용했습니까?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "비밀번호" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "방에 접근을 제한하기 위한 비밀번호" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 #, fuzzy msgid "Moderated" msgstr "조정됨" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "발언 가능한 참여자만이 메세지를 보낼 수 있습니다" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "회원 전용" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "회원만이 방에 들어올 수 있습니다" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "메세지 기록" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "최대 로그 수가 방에 의해 제한됩니다" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "방 설정" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Dino에 오신 것을 환영합니다!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "로그인 또는 계정을 생성 하십시오." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "계정 설정" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "활성화 된 계정이 없습니다" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "계정 관리" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "잘못된 비밀번호" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "올바르지 않은 TLS 인증서" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "%s 계정을 삭제하시겠습니까?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "삭제" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "아바타 선택" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "이미지" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "모든 파일" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "연결 됨" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "연결 해제 됨" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "오류" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "계정 추가" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "보안 인증서가 운영체제로부터 신뢰되지 않았습니다" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 #, fuzzy msgid "Its security certificate is issued to another domain." msgstr "보안 인증서가 다른 도메인으로부터 발급되었습니다." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "보안 인증서가 미래에 유효합니다." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "보안 인증서가 만료되었습니다." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "%s에 로그인" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "이제 %s 계정을 쓸 수 있습니다." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "잘못된 유저명이거나 비밀번호" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 #, fuzzy msgid "Something went wrong" msgstr "무엇인가 잘 못 되었습니다." #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "서버로부터 응답이 없습니다" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "%s에 등록" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "이 서버는 웹사이트를 통한 가입만이 가능합니다" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "웹사이트 열기" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "등록" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "가입하는 법에 관한 정보는 %s를 확인하세요" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "열기" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "로 저장…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s 그리고 %i 사람들이 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s와(과) %s, %s이(가) 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s와(과) %s이(가) 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s 이(가) 입력 중…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "통화 시작 됨" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "%s 전에 시작 됨" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "다른 장치로 통화를 제어하고 있습니다" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "통화 종료 됨" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "%s에 종료 됨" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, fuzzy, c-format msgid "Lasted %s" msgstr "마지막 내역 %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 #, fuzzy msgid "Call missed" msgstr "부재된 통화" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "통화를 받지 않았습니다" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s이(가) 통화를 받지 않음" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "통화 거부됨" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "통화를 거부했습니다" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s이(가) 통화를 거부했습니다" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "통화 실패" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i 시간" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i 분" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "몇 초 전" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "이 연락처가 당신을 연락처에 추가하길 원합니다" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "%s로 다운로드 중…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, fuzzy, c-format msgid "%s offered: %s" msgstr "%s 전송 됨: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, fuzzy, c-format msgid "File offered: %s" msgstr "파일 전송됨: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "파일 전송 됨" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "전송 실패" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "메세지 수정" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "열린 채팅이 없습니다" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "계정" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "닉네임" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "가칭" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "연락처 추가" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "새로운 메세지 수신시에 알림" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "웃음 모양을 이모티콘으로 변환" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "맞춤법 확인" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "현대 XMPP 채팅 클라이언트" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino는 데스크탑을 위한 현대 오픈소스 채팅 클라이언트입니다. 깔끔하고 신뢰 할 " "수 있는 Jabber/XMPP 경험을 개인정보 보호 중시와 함께 제공할 수 있도록 주력하" "고 있습니다." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO와 OpenPGP를 통한 종단간 암호화를 지원하며 프라이버시와 관련된 읽음 확인" "이나 입력 알림기능을 설정할 수 있습니다." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino는 서버와 타 장치와의 메세지 동기화로부터 기록을 불러옵니다." #: main/data/global_search.ui:27 #, fuzzy msgid "No active search" msgstr "검색 결과가 없음" #: main/data/global_search.ui:28 #, fuzzy msgid "Type to start a search" msgstr "검색하려면 입력하세요" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "해당하는 메세지가 없음" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "오타를 확인해보거나 필터를 제거해보세요" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "보내기" #: main/data/shortcuts.ui:10 msgid "General" msgstr "전역" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "대화" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "창 제어" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "다음 대화로 넘어가기" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "이전 대화로 넘어가기" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "계정" #: main/data/manage_accounts/dialog.ui:176 #, fuzzy msgid "Local alias" msgstr "내부 가칭" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "어떤 계정도 설정되지 않음" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "계정 추가" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "가입" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "계정 생성" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "보안 연결을 설정 할 수 없음" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "연결" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "공용 서버 선택" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "또는 구체적인 서버 주소 입력" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "로그인 하기" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "다른 서버 선택" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "모든 설정이 끝났습니다!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "완료" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "대화를 시작하거나 채널에 들어가려면 여기를 누르세요." dino-0.4.3/main/po/lb.po0000644000000000000000000011050414452563620013454 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: Luxembourgish (Dino)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-15 00:11+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Noriicht ze laang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "beaarbecht" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Onverschlësselt" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Konnt de Message net schécken" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i Minutt hier" msgstr[1] "%i Minutten hier" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Just elo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ech" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild geschéckt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Datei geschéckt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild kritt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Datei kritt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gëschter" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Konversatioun starten" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Besëtzer" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrateur" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Member" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Benotzer" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Alueden" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Privat Conversatioun starten" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Erausgeheien" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitéieren an Konferenz" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Auswielen" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Ofbriechen" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s vum %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Messagen duerchsichen" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Memberen" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Verbannen…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Start" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Kanal bäitrieden" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Nächst" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Bäitrieden" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Zeréck" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bäitrieden…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Passwuert gëtt gebraucht fir de Raum ze betrieden" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Gebannt vum bäitrieden an erstelle vu Konferenzen" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Raum existéiert net" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Net erlaabt ee Raum ze erstellen" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Raum ass nëmme fir Memberen" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wiel een anere Spëtznumm" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "De Raum huet ze vill Benotzer" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Konnt net op %s connectéieren" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ongülteg Adress" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Bäisetzen" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Tastaturofkierzungen" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Iwwert Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Haut" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i Sichresultat" msgstr[1] "%i Sichresultater" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "An %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Mat %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akzeptéieren" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Abonnement Ufro" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Verweigeren" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Aluedung an %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s huet dech zu %s agelueden" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Astellungen" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokal Astellungen" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Tipp-Notifikatioune schécken" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Scheck Lies Bestätegungen" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notifikatiounen" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Un" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Aus" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Nëmme wann erwäänt" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferenz Detailer" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetailer" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blockéieren" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Kommunikatioun a Status Updates sinn an all Richtung blockéiert" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Numm vum Raum" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Beschreiwung vum Raum" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Dauerhaft" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "De Raum bleift bestoen, nodeems de leschte Benotzer erausgaangen ass" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Ëffentlech siichtbar" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Benotzer däerfe Sujet änneren" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Erlabnis JIDs ze gesinn" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wien ass erlaabt den Gäscht hier JIDs ze gesinn?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Passwuert" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Ee Passwuert fir den Accès zu dësem Raum anzeschränken" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderéiert" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Nëmme Benotzer mat Stemm däerfe Messagë schécken" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Nëmme fir Memberen" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Nëmme Memberen däerfen de Raum betrieden" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Messagen Historique" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maximal Unzuel vum ale Messagen, déi vum Raum geschéckt ginn" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Raum Astellungen" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Wëllkomm bei Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Logg dech an oder kreéier een Account fir lass ze leen." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Konto opsetzen" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Keng Kontoen aktiv" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Kontoe managen" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Falsch Passwuert" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Invaliden TLS Zertifikat" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Konto %s läschen?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Läschen" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Avatar auswielen" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Biller" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "All Dateien" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Verbonnen" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Getrennt" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Feeler" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Account bäisetzen" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Bei %s umellen" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Du kanns elo den Account %s benotzen." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Falsche Benotzernumm oder Passwuert" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Eppes ass schif gaangen" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Keng Äntwert vum Server" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Ob %s registréieren" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "De Server setzt eng Registréierung duerch eng Websäit viraus" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Websäit opmaachen" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registréieren" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Kuck %s fir Informatioune iwwert Registréierung" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, fuzzy, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s an %i anerer sinn um schreiwen" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s an %s sinn um schreiwen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s an %s sinn um schreiwen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s ass um schreiwen…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Dëse Kontakt wéilt dech gären an seng Kontaktlecht ophuelen" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Lueden %s erof…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s offréiert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Datei offréiert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Datei offréiert" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Dateitransfert ass feelgeschloen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Datei" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "De Message updaten" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du hues keng oppen Chats" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Spëtznumm" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonym" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontakt bäisetzen" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Benoriichtege wann een neie Message ukënnt" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Smileyen an Emojien konvertéieren" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Modernen XMPP Chat Client" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng " "opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op " "Privatsphär." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält " "Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat " "anere Geräter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Keng aktiv Sich" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tipp fir eng Sich ze starten" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Keng Message fonnt" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Iwwerpréif Schreifweis oder läsch Filteren" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Allgemeng" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigatioun" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Zu nächster Konversatioun sprangen" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Zu viregter Konversatioun sprangen" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Kontoen" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokalen Pseudonym" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Keng Kontoe konfiguréiert" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Ee Konto bäisetzen" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Aloggen" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Account kreéieren" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Konnt keng geséchert Verbindung opbauen" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connectéieren" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Ëffentleche Server auswielen" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Oder spezifizéier eng Server Adress" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Aloggen a Platz" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Anere Server auswielen" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Alles ageriicht!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Ofschléissen" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klick hei fir eng Konversatioun ze starten oder engem Channel " #~ "bäizetrieden." #~ msgid "No active conversations" #~ msgstr "Keng Conversatiounen aktiv" #~ msgid "Main window with conversations" #~ msgstr "Haaptfënster mat den Conversatiounen" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s an %i anerer" #~ msgid "You can now start using %s" #~ msgstr "Du kanns elo %s benotzen" #~ msgid "Open Registration" #~ msgstr "Oppe Registréierung" #~ msgid "Save" #~ msgstr "Späicheren" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s an %s" #~ msgid "%s and %s" #~ msgstr "%s an %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippt grad…" #~ msgstr[1] "tippe grad…" #~ msgid "has stopped typing" #~ msgstr "huet opgehalen ze tippen" #~ msgid "%i search results" #~ msgstr "%i Sichresultater" #~ msgid "Discover real JIDs" #~ msgstr "Echt JIDs gesinn" #~ msgid "Who may discover real JIDs?" #~ msgstr "Ween däerf echt JIDs gesinn?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passwuert fir de Raum ze betrieden, wann een gebraucht gëtt" #~ msgid "Failed connecting to %s" #~ msgstr "Feeler beim connectéieren op %s" #~ msgid "Join Conference" #~ msgstr "Konferenz bäitrieden" #~ msgid "Communicate happiness." #~ msgstr "Gléck kommunizéieren." #~ msgid "Preferences" #~ msgstr "Astellungen" #~ msgid "Quit" #~ msgstr "Zoumaachen" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID sollt folgend Form hunn „user@example.com“" #~ msgid "Copy Link Address" #~ msgstr "Kopéier Link Adress" #~ msgid "Copy" #~ msgstr "Kopéieren" #~ msgid "Select All" #~ msgstr "Alles auswielen" #~ msgid "Search" #~ msgstr "Sichen" #~ msgid "Send message marker" #~ msgstr "Message Marker verschécken" #~ msgid "Start Chat" #~ msgstr "Chat starten" #~ msgid "Request presence updates" #~ msgstr "Presenz Updaten ufroen" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "" #~ "Dat heiten ass eng App-Notifikatioun. Klick de Knäppche fir ze ewech ze " #~ "maachen" dino-0.4.3/main/po/lt.po0000644000000000000000000011326214452563620013502 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-10-18 19:07+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > " "19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? " "1 : 2);\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Failas viršija didžiausią įkėlimo į serverį dydį." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Žinutė per ilga" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "redaguota" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "laukiama…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "pristatymas nepavyko" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifruota" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Nepavyko išsiųsti žinutės" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "prieš %i minutę" msgstr[1] "prieš %i minutes" msgstr[2] "prieš %i minučių" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Ką tik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Aš" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Paveikslas išsiųstas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Failas išsiųstas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Paveikslas gautas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Failas gautas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Išeinantis skambutis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Įeinantis skambutis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Vakar" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Pradėti pokalbį" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Savininkas" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administratorius" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Narys" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Naudotojas" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Pakviesti" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Pradėti privatų pokalbį" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Išvaryti" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Suteikti leidimą rašyti" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Panaikinti leidimą rašyti" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Pakviesti į konferenciją" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Pasirinkti failą" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Pasirinkti" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Atsisakyti" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s iš %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ši konferencija jums neleidžia siųsti žinučių." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Užklausti leidimo" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Siųsti failą" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Pradėti skambutį" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Garso skambutis" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Vaizdo skambutis" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Ieškoti žinučių" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Nariai" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Derinimo informacija" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Skambinama…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Skamba…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Jungiamasi…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s baigė pokalbį" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s atmetė skambutį" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameros" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nerasta jokios kameros." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonai" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nerasta jokio mikrofono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Garsiakalbiai" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nerasta jokių garsiakalbių." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Pakviesti į skambutį" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Pradėti" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Prisijungti prie kanalo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Kitas" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Prisijungti" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Atgal" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Prisijungiama…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Norint užeiti į kambarį, reikalingas slaptažodis" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Uždrausta prisijungti ar sukurti konferenciją" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Kambario nėra" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Neleidžiama kurti kambarių" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Kambarys tik nariams" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Pasirinkti kitą slapyvardį" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Kambaryje per daug žmonių" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Nepavyko prisijungti prie %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Neteisingas adresas" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Pridėti" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Trumpiniai" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Apie Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Šiandien" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%b %d, %a" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i paieškos rezultatas" msgstr[1] "%i paieškos rezultatai" msgstr[2] "%i paieškos rezultatų" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Ties %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Su %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Įeinantis vaizdo skambutis" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Įeinantis grupės vaizdo skambutis" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Įeinantis grupės skambutis" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Atmesti" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Priimti" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Prenumeratos užklausa" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Atmesti" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Pakvietimas į %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s pakvietė jus į %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Leidimo užklausa" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s prašo leidimo rašyti ties %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Nustatymai" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Vietiniai nustatymai" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Siųsti pranešimus apie žinutės rašymą" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Siųsti pranešimus apie žinučių skaitymą" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Pranešimai" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Įjungta" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Išjungta" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Tik paminėjus" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Pagal numatymą: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Prašyti" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Leidimai" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Prašyti leidimo siųsti žinutes" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Išsamiau apie konferenciją" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Išsamiau apie adresatą" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blokuoti" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Abiejomis kryptimis bus blokuojamas bendravimas ir būsenos atnaujinimai" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Kambario pavadinimas" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Kambario aprašas" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Ilgalaikis" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Kambarys išliks netgi po to, kai išeis paskutinis lankytojas" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Viešai randamas" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Lankytojai gali keisti temą" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Leidimas žiūrėti JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kam leidžiama matyti lankytojų JID?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Slaptažodis" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Slaptažodis, apribojantis prieigą prie kambario" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderuojamas" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Tik lankytojai su balso teise gali siųsti žinutes" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Tik nariai" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Tik nariai gali užeiti į kambarį" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Žinučių istorija" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Didžiausias kambario išduodamas praeities žurnalo skaičius" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Kambario konfigūracija" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Sveiki atvykę į Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Norėdami pradėti, prisijunkite arba susikurkite paskyrą." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Nusistatyti paskyrą" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Aktyvių paskyrų nėra" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Tvarkyti paskyras" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Neteisingas slaptažodis" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Negaliojantis TLS liudijimas" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Šalinti paskyrą %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Šalinti" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Pasirinkti avatarą" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Paveikslai" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Visi failai" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Prisijungta" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Atsijungta" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Klaida" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Pridėti paskyrą" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Serveriui nepavyko įrodyti, kad jis yra %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Jūsų operacinė sistema nepasitiki jo saugumo liudijimu." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Jo saugumo liudijimas yra išduotas kitai sričiai." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Jo saugumo liudijimas pradės galioti tik ateityje." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Jo saugumo liudijimas nebegalioja." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Prisijungti prie %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Dabar galite naudoti paskyrą %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Neteisingas naudotojo vardas ar slaptažodis" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Kažkas nutiko" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Nėra atsakymo iš serverio" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registruotis %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Serveris reikalauja registruotis per internetinę svetainę" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Atverti internetinę svetainę" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registruotis" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Išsamesnei informacijai apie registraciją, žiūrėkite %s" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Atidaryti" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Išsaugoti kaip…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ir dar %i rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ir %s rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s ir %s rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s rašo…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Pokalbis prasidėjo" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Pradėta prieš %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Šį skambutį atlikote kitame įrenginyje" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Pokalbis baigėsi" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Baigėsi %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Truko %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Praleistas skambutis" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Praleidote šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s praleido šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Skambutis atmestas" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Atmetėte šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s atmetė šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Skambutis nepavyko" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i valandą" msgstr[1] "%i valandas" msgstr[2] "%i valandų" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minutę" msgstr[1] "%i minutes" msgstr[2] "%i minučių" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "kelias sekundes" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Šis adresatas norėtų jus pridėti į savo adresatų sąrašą" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Atsisiunčiama %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s pasiūlė: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Pasiūlytas failas: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Pasiūlytas failas" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Failo persiuntimas nepavyko" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atnaujinti žinutę" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Neturite jokių atvertų pokalbių" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Paskyra" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Slapyvardis" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonimas" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Pridėti adresatą" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Pranešti, kai gaunama nauja žinutė" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Keisti šypsenėles į jaustukus" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Tikrinti rašybą" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Šiuolaikinė XMPP pokalbių kliento programa" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos " "pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą " "nepamirštant apie jūsų privatumą." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia " "konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie " "žinučių skaitymą ir rašymą." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais " "įrenginiais." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nėra aktyvios paieškos" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Rašykite norėdami atlikti paiešką" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nėra atitinkančių žinučių" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Patikrinkite rašybą arba pabandykite pašalinti filtrus" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Siųsti" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Bendri" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Pokalbis" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Naršymas" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Peršokti prie kito pokalbio" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Peršokti prie ankstesnio pokalbio" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Paskyros" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Vietinis pseudonimas" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nėra sukonfigūruotų paskyrų" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Pridėti paskyrą" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Prisijungti" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Sukurti paskyrą" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Nepavyko užmegzti saugaus ryšio" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Prisijungti" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Pasirinkite viešąjį serverį" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Arba nurodykite serverio adresą" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Vietoj to, prisijungti" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Pasirinkti kitą serverį" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Viskas nustatyta!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Užbaigti" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Spustelėkite čia norėdami pradėti pokalbį ar prisijungti prie kanalo." #~ msgid "Video call incoming" #~ msgstr "Įeinantis vaizdo skambutis" #~ msgid "Call incoming" #~ msgstr "Įeinantis skambutis" #~ msgid "Establishing call" #~ msgstr "Rengiamasi pokalbiui" #~ msgid "Video call establishing" #~ msgstr "Rengiamasi vaizdo skambučiui" #~ msgid "Call establishing" #~ msgstr "Rengiamasi pokalbiui" #~ msgid "Call in progress…" #~ msgstr "Vyksta pokalbis…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Truko %s" #~ msgid "seconds" #~ msgstr "sek." #~ msgid "No active conversations" #~ msgstr "Aktyvių pokalbių nėra" #~ msgid "Main window with conversations" #~ msgstr "Pagrindinis langas su pokalbiais" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s ir dar %i" #~ msgid "You can now start using %s" #~ msgstr "Dabar, galite pradėti naudoti %s" #~ msgid "Open Registration" #~ msgstr "Atverti registraciją" #~ msgid "Save" #~ msgstr "Įrašyti" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ir %s" #~ msgid "%s and %s" #~ msgstr "%s ir %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "rašo…" #~ msgstr[1] "rašo…" #~ msgstr[2] "rašo…" #~ msgid "has stopped typing" #~ msgstr "nustojo rašyti" #~ msgid "%i search results" #~ msgstr "Paieškos rezultatų: %i" #~ msgid "Discover real JIDs" #~ msgstr "Atrasti tikruosius JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kas gali atrasti tikruosius JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Jei nustatytas, norint užeiti į kambarį bus reikalaujama slaptažodžio" dino-0.4.3/main/po/lv.po0000644000000000000000000007550714452563620013515 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-02-07 20:25+0000\n" "Language-Team: none\n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= " "19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Ziņojums ir pārāk garš" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "rediģēja" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "neizdevās nosūtīt" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifrēts" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/nb.po0000644000000000000000000011244214452563620013461 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-11 18:58+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 #, fuzzy msgid "The file exceeds the server's maximum upload size." msgstr "Filen er større enn opplastingsstørrelsen som tillates av tjeneren." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Meldingen er for lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "redigert" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "venter …" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "levering mislyktes" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ukryptert" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Kunneikke sende melding" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minutt siden" msgstr[1] "%i minutter siden" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Akkurat nå" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Meg" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bilde sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bilde mottatt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil mottatt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Utgående anrop" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Innkommende anrop" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "I går" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Start samtale" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eier" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Medlem" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Bruker" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviter" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Start privat samtale" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kast ut" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Innvilg skrivetilgang" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Tilbakekall skrivetilgang" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviter til konferanse" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Velg fil" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Velg" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Avbryt" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s fra %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Du tillates ikke å sende meldinger i denne konferansen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Forespør tilgang" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Send fil" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Start samtale" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Lydsamtale" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videoanrop" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Søk i meldinger" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Medlemmer" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Ringer …" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Ringer …" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Kobler til…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s avsluttet samtalen" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s avslo samtalen" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofoner" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Høyttalere" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 #, fuzzy msgid "Invite to Call" msgstr "Inviter til samtalen" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Start" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Ta del i kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Neste" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Ta del" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Tilbake" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Tar del…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Passord kreves for å ta del i rommet" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Bannlyst fra å ta del i eller å opprette konferanser" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Rommet finnes ikke" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ikke tillatt å opprette rom" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Rommet er kun for medlemmer" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Velg et annet kallenavn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Rommet har for mange deltagere" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Kunne ikke koble til %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ugyldig adresse" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Legg til" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Tastatursnarveier" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Om Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "I dag" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i søkeresultat" msgstr[1] "%i søkeresultater" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "I %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Med %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Innkommende videosamtale" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Innkommende videogruppeanrop" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Innkommende gruppeanrop" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 #, fuzzy msgid "Reject" msgstr "Avslå" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Godta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Abonnementsforespørsel" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Nekt" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitasjon til %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s inviterte deg til %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Tilgangsforespørsel" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, fuzzy, c-format msgid "%s requests the permission to write in %s" msgstr "%s forespør tilgang til å skrive til %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Innstillinger" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokale innstillinger" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Send skrivevarsling" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Send meldingskvitteringer" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Merknader" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "På" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Av" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Bare når nevnt" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Forvalg: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Forespør" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Tilganger" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Forespør tilgang til meldingsforsendelse" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferansedetaljer" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetaljer" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blokker" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Kommunikasjon og statusoppdateringer i begge retninger er blokkert" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Romnavn" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Rombeskrivelse" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Vedvarende" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Rommet vil vedvare etter at siste deltager forlater" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Offentlig søkbart" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Deltagere kan endre emne" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Tilgang til visning av JID-er" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Hvem skal tillates å vise brukernes JID-er?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Passord" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Et passord for å begrense tilgang til rommet" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderert" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Bare deltagere med stemme kan sende meldinger" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Kun for medlemmer" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Kun medlemmer har adgang til rommet" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Meldingshistorikk" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maksimalt mengde historikk som skal utstedes av rommet" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Rom-oppsett" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Velkommen til Dino." #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Logg inn eller opprett en konto for å starte." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Sett opp konto" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Ingen kontoer aktive" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Behandle kontoer" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Feil passord" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Ugyldig TLS-sertifikat" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Fjern konto %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Fjern" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Velg avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Bilder" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Alle filer" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Tilkoblet" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Frakoblet" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Feil" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Legg til konto" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Tjeneren kunne ikke bevise at det er %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Ditt operativsystem mangler tiltro til dets sikkerhetssertifikat." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Dets sikkerhetssertifikat er utstedt til et annet domene." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Dets sikkerhetssertifikat vil kun bli gyldig i fremtiden." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Dets sikkerhetssertifikat er utløpt." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Logg inn på %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Du kan nå bruke kontoen %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Feil brukernavn eller passord" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Noe gikk galt" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Inget svar fra tjener" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrer på %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Tjeneren krever at man logger inn via en nettside" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Åpne nettside" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrer" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Se %s for å lære hvordan du registrerer deg" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Åpne" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Lagre som …" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s og %i andre skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s skriver…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Samtale startet" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Startet for %s siden" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Du håndterte denne samtalen på en annen enhet" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Samtale avsluttet" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Avsluttet %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, fuzzy, c-format msgid "Lasted %s" msgstr "Varte %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Tapt anrop" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Du besvarte ikke dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s tok ikke dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Anrop avvist" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Du avslo dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s avslo dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Anrop mislyktes" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "én time" msgstr[1] "%i timer" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "ett minutt" msgstr[1] "%i minutter" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "et par sekunder" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Denne kontakten ønsker å legge deg til på kontaktlisten sin" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Laster ned %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s tilbød: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fil tilbudt: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fil tilbudt" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Filoverføring mislyktes" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fil" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Oppdater melding" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du har ingen åpne sludringer" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Kallenavn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Legg til kontakt" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Varsle ved mottak av ny melding" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Konverter smilefjes til emoji-er" #: main/data/settings_dialog.ui:59 #, fuzzy msgid "Check spelling" msgstr "Stavekontroll" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Moderne XMPP-sludreklient" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer " "på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om " "personvernet." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater " "oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og " "skrivevarsling." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino henter historikk fra tjeneren og synkroniserer meldinger med andre " "enheter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Inget aktivt søk" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skriv for å starte et søk" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ingen samsvarende meldinger" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Sjekk stavingen eller prøv å fjerne filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Send" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Generelt" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Samtale" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigasjon" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Hopp til neste samtale" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Hopp til forrige samtale" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Kontoer" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokalt alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Ingen kontoer satt opp" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Legg til en konto" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Logg inn" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Opprett konto" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Kunne ikke etablere sikker forbindelse" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Koble til" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Velg en offentlig tjener" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Eller angi en tjeneradresse" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Logg inn istedenfor" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Velg en annen tjener" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Ferdig oppsatt." #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Fullfør" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klikk her for å starte en samtale, eller ta del i en kanal." #~ msgid "No active conversations" #~ msgstr "Ingen kontoer aktive" #~ msgid "Main window with conversations" #~ msgstr "Hovedvindu med samtaler" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s og %i andre" #~ msgid "You can now start using %s" #~ msgstr "Du kan nå begynne å bruke %s" #~ msgid "Open Registration" #~ msgstr "Åpne registrering" #~ msgid "Save" #~ msgstr "Lagre" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s og %s" #~ msgid "%s and %s" #~ msgstr "%s og %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "skriver…" #~ msgstr[1] "skriver…" #~ msgid "has stopped typing" #~ msgstr "har tatt pause i skrivinga" #~ msgid "%i search results" #~ msgstr "%i søkeresultater" #~ msgid "Discover real JIDs" #~ msgstr "Oppdag ekte JID-er" #~ msgid "Who may discover real JIDs?" #~ msgstr "Hvem har tilgang til oppdagelse av ekte JID-er?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passord som kreves for å ta del i rommet. La stå tomt for inget" #~ msgid "Failed connecting to %s" #~ msgstr "Klarte ikke å koble til %s" #~ msgid "Join Conference" #~ msgstr "Ta del i konferanse" #~ msgid "Communicate happiness." #~ msgstr "Kommuniser glede." #~ msgid "Preferences" #~ msgstr "Innstillinger" #~ msgid "Quit" #~ msgstr "Avslutt" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID skal være av typen \"bruker@eksmepel.no\"" #~ msgid "Copy Link Address" #~ msgstr "Kopier lenkeadresse" #~ msgid "Copy" #~ msgstr "Kopier" #~ msgid "Select All" #~ msgstr "Velg alle" #~ msgid "Search" #~ msgstr "Søk" #~ msgid "Send message marker" #~ msgstr "Send meldingsmarkør" #~ msgid "Start Chat" #~ msgstr "Start sludring" #~ msgid "Request presence updates" #~ msgstr "Forespør tilstedeværelsesoppdateringer" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Dette er en merknad i programmet. Klikk på knappen for å avvise" #~ msgid "Join on startup" #~ msgstr "Ta del ved oppstart" #~ msgid "Add Chat" #~ msgstr "Legg til sludring" dino-0.4.3/main/po/nl.po0000644000000000000000000011513614452563620013476 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Het bestand overschrijdt de maximaal toegestane grootte." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Het bericht is te lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "bewerkt" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "in wachtrij…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "verzenden mislukt" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Onversleuteld" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Het bericht kan niet worden verstuurd" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuut geleden" msgstr[1] "%i minuten geleden" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Zojuist" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Afbeelding verstuurd" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Bestand verstuurd" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Afbeelding ontvangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Bestand ontvangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Uitgaande oproep" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Inkomende oproep" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gisteren" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Gesprek starten" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigenaar" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Beheerder" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Lid" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Gebruiker" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Uitnodigen" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Privégesprek starten" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Wegsturen" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Schrijfmachtiging verlenen" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Schrijfmachtiging weigeren" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Uitnodigen voor groepsgesprek" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Kies een bestand" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Kiezen" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annuleren" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s op %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "In deze conferentie mag je geen berichten versturen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Toestemming vragen" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Verstuur een bestand" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Bellen" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audiogesprek" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videogesprek" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Zoeken naar berichten" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Deelnemers" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Foutopsporingsinformatie" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Bezig met bellen…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Bezig met overgaan…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Bezig met verbinden…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s heeft de oproep beëindigd" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s hebt/heeft de oproep geweigerd" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camera's" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Geen camera aangetroffen." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoons" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Geen microfoon aangetroffen." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Luidsprekers" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Geen luidspreker aangetroffen." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Uitnodigen voor gesprek" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Starten" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Deelnemen aan kanaal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Volgende" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Deelnemen" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Terug" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bezig met deelnemen…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Wachtwoord voor groepsgesprek vereist" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Deelnemen aan of aanmaken van groepsgesprekken verboden" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Groepsgesprek bestaat niet" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Aanmaken van groepsgesprek niet toegestaan" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Groepsgesprek is alleen voor leden" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Kies een andere bijnaam" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Groepsgesprek heeft te veel deelnemers" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Kan geen verbinding maken met %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ongeldig adres" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Toevoegen" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Over Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Vandaag" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i zoekresultaat" msgstr[1] "%i zoekresultaten" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Op %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Met %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Inkomende video-oproep" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Inkomend videogroepsgesprek" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Inkomend groepsgesprek" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Afwijzen" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Toestaan" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Abonneerverzoek" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Weigeren" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Uitnodiging voor %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s heeft je uitgenodigd voor %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Machtigingsverzoek" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s verzoekt toestemming om te mogen schrijven in %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Instellingen" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokale instellingen" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Aan-het-typenmeldingen versturen" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Leesbevestigingen versturen" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Meldingen" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Gesprek vastmaken" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Maakt het gesprek bovenaan de gesprekslijst vast" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Aan" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Uit" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Alleen bij vermeldingen" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Standaard: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Verzoek sturen" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Machtigingen" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Vraag toestemming om berichten te versturen" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Groepsgesprekinformatie" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Contactpersooninformatie" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blokkeren" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Communicatie en statusupdates worden in beide richtingen geblokkeerd" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Naam van groepsgesprek" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Beschrijving van groepsgesprek" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Blijvend" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Het groepsgesprek blijft bestaan als de laatste deelnemer het verlaat" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Openbaar" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Deelnemers mogen het onderwerp aanpassen" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Toestemming om JID's te bekijken" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wie mag de JID's bekijken?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Wachtwoord" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Een wachtwoord om toegang tot het gesprek te beperken" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Gemodereerd" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Enkel deelnemers met stem mogen berichten sturen" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Enkel leden" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Enkel leden mogen deelnemen aan het groepsgesprek" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Berichtgeschiedenis" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Het maximumaantal berichten dat het groepsgesprek toont" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Gespreksinstellingen" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Welkom bij Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Log in of maak een account om aan de slag te gaan." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Account instellen" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Geen actieve accounts" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Accounts beheren" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Onjuist wachtwoord" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Ongeldig TLS-certificaat" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Wil je '%s' verwijderen?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Verwijderen" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Kies een profielfoto" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Afbeeldingen" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Alle bestanden" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Verbonden" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Verbinding verbroken" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Fout" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Account toevoegen" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "De server kan niet aantonen dat het %s is." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Je besturingssysteem vertrouwt het beveiligingscertificaat niet." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Het beveiligingscertificaat is afgegeven voor een ander domein." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Het beveiligingscertificaat is pas geldig vanaf een latere datum." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Het beveiligingscertificaat is verlopen." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Inloggen op %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Je kunt '%s' nu gebruiken." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Onjuiste gebruikersnaam of wachtwoord" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Er is iets misgegaan" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Geen antwoord van server" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registreren op %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "De server vereist registratie via een website" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Website openen" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registreren" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ga naar %s voor meer informatie over het registreren" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Bericht bewerken" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ik" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Reactie toekennen" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Openen" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Opslaan als…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s en %i anderen zijn aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s en %s zijn aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s zijn %s aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s is aan het typen…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Oproep gestart" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Begonnen: %s geleden" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Deze oproep is afgehandeld op een ander apparaat" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Het gesprek is beëindigd" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Beëindigd: %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Duur: %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Je hebt deze oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s heeft deze oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Je hebt deze oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s heeft deze oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Oproep mislukt" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i uur" msgstr[1] "%i uur" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuut" msgstr[1] "%i minuten" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "enkele seconden" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Deze contactpersoon wil je toevoegen aan zijn/haar lijst" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Beantwoorden" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Bezig met downloaden van %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s biedt aan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Bestand aangeboden: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Bestand aangeboden" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Bestandsoverdracht mislukt" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Bestand" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Bericht bijwerken" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Je hebt geen geopende gesprekken" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klik op ‘+’ om een gesprek te starten of deel te nemen aan een kanaal" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Account" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Bijnaam" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Contactpersoon toevoegen" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Melding tonen bij nieuw bericht" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Smileys omzetten naar emoji’s" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Spelling controleren" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Een moderne XMPP-chatclient" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een " "eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het " "achterhoofd." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je " "toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-" "typenmeldingen, in te stellen." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino haalt de geschiedenis op van de server en synchroniseert berichten met " "andere apparaten." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Geen actieve zoekopdracht" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Typ om te beginnen met zoeken" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Geen overeenkomende berichten" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controleer de spelling of verwijder filters" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Versturen" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Algemeen" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Gesprek" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigatie" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ga naar volgend gesprek" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ga naar vorig gesprek" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Accounts" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokale alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Geen accounts ingesteld" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Account toevoegen" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Inloggen" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Account aanmaken" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Kan geen beveiligde verbinding opzetten" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Verbinden" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Kies een openbare server" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Of voer een serveradres in" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Inloggen op account" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Andere server kiezen" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Klaar!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Voltooien" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klik hier om een gesprek te starten of deel te nemen aan een kanaal." #~ msgid "Video call incoming" #~ msgstr "Inkomend videogesprek" #~ msgid "Call incoming" #~ msgstr "Inkomende oproep" #~ msgid "Establishing call" #~ msgstr "Bezig met verbinden…" #~ msgid "Video call establishing" #~ msgstr "Bezig met verbinden…" #~ msgid "Call establishing" #~ msgstr "Bezig met verbinden…" #~ msgid "Call in progress…" #~ msgstr "Bezig met bellen…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Duur: %s" #~ msgid "seconds" #~ msgstr "seconden" #~ msgid "No active conversations" #~ msgstr "Geen gesprekken actief" #~ msgid "Main window with conversations" #~ msgstr "Hoofdvenster met gesprekken" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s en %i anderen" #~ msgid "You can now start using %s" #~ msgstr "U kunt %s nu beginnen gebruiken" #~ msgid "Open Registration" #~ msgstr "Open registratie" #~ msgid "Save" #~ msgstr "Opslaan" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s en %s" #~ msgid "%s and %s" #~ msgstr "%s en %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "is aan het typen…" #~ msgstr[1] "zijn aan het typen…" #~ msgid "has stopped typing" #~ msgstr "is gestopt met typen" #~ msgid "%i search results" #~ msgstr "%i zoekresultaten" #~ msgid "Discover real JIDs" #~ msgstr "Zichtbaarheid van JID’s" #~ msgid "Who may discover real JIDs?" #~ msgstr "Wie mag de JID’s van de deelnemers zien?" #~ msgid "Password required for room entry, if any" #~ msgstr "Wachtwoord voor groepsgesprek, indien nodig" #~ msgid "Failed connecting to %s" #~ msgstr "Verbinden met %s mislukt" #~ msgid "Join Conference" #~ msgstr "Deelnemen aan groepsgesprek" #~ msgid "Communicate happiness." #~ msgstr "Communiceer geluk." #~ msgid "Preferences" #~ msgstr "Voorkeuren" #~ msgid "Quit" #~ msgstr "Afsluiten" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID moet de vorm ‘gebruiker@voorbeeld.nl’ volgen" #~ msgid "Copy Link Address" #~ msgstr "Verwijzingsadres kopiëren" #~ msgid "Copy" #~ msgstr "Kopiëren" #~ msgid "Select All" #~ msgstr "Alles selecteren" #~ msgid "Search" #~ msgstr "Zoeken" #~ msgid "Send message marker" #~ msgstr "Berichtmarkers sturen" #~ msgid "Start Chat" #~ msgstr "Gesprek beginnen" #~ msgid "Request presence updates" #~ msgstr "Vraag om aanwezigheidsupdates" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Dit is een melding. Klik om te sluiten" #~ msgid "Join on startup" #~ msgstr "Deelnemen bij opstarten" #~ msgid "Add Chat" #~ msgstr "Gesprek beginnen" dino-0.4.3/main/po/oc.po0000644000000000000000000011227114452563620013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-04-21 12:15+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" "Lo fichièr es mai pesuc que la talha maximala de mandadís sul servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Lo messatge es tròp long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "modificat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "mandadís…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "fracàs del mandadís" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Pas chifrat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Mandadís del messatge impossible" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fa %i minuta" msgstr[1] "fa %i minutas" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Ara meteis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ieu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imatge enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fichièr enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imatge recebuda" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fichièr recebut" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Sonada sortissenta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Sonada dintranta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ièr" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Començar la discussion" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietari" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizaire" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Començar una discussion privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Autorizar l’escritura" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar l’autorizacion d’ecritura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar a la conferéncia" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Seleccionar fichièr" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Seleccionar" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anullar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Aquesta conferéncia vos autoriza pas a enviar de messatges." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demanda d’autorizacion" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Enviar un fichièr" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Aviar una sonada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Sonada àudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Sonada vidèo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Cercar de messatges" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Sonada…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Tinda…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Connexion…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s a arrestat la sonada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s a regetat la sonada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Cameràs" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Se trapèt pas cap de camerà." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfòns" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Se trapèt pas cap de microfòn." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Nautparlaires" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Se trapèt pas cap de nautparlaire." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar a la sonada" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Començar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Rejónher una sala" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Seguent" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Rejónher" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Tornar" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Connexion a la sala…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Senhal requesit per dintrar dins la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Sètz forabandit e podètz pas participar o crear de conferéncias" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala existís pas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Creacion de sala defenduda" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala solament pels membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Causissètz un escais-nom diferent" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tròp de participants a la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Connexion impossibla a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "L’adreça es invalida" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ajustar" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Acorchis clavièr" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "A prepaus de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Uèi" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultat de recèrca" msgstr[1] "%i resultats de recèrca" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Amb %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Sonada vidèo dintranta" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Sonada vidèo de grop dintranta" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Sonada de grop dintranta" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Regetar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Demanda d’abonament" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Regetar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Convit a %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s vos convidèt a %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Demanda d’autorizacion" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demanda l’autorizacion d’escriure dins %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Paramètres" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Paramètres locals" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Enviar una notificacion quand escrivètz" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Enviar un acusat de lectura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificacions" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Activat" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Desactivat" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Solament quand vos mencionan" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Per defaut : %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Demandar" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permissions" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Demandar la permission d’enviar de messatges" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalhs de la conferéncia" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhs del contacte" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blocar" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Totes los escambis de messatges e d’estat son blocats dins las doas " "direccions" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nom de la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descripcion de la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistent" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "La sala demorarà après la sortida del darrièr ocupant" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Discussion publica" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Los ocupants pòdon cambiar lo subjècte" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permission de veire los JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qual pòt veire los JID dels participants ?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Senhal" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Un senhal per restrénher l’accès a sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderada" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Solament los ocupant amb votz pòdon enviar de messatges" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Membres solament" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Solament los membres pòdon dintrar a la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Istoric dels messatges" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Nombre maximum de messatge tornats per la sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuracion de la sala" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "La benvenguda a Dino !" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Connectatz-vos o creatz un compte per començar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurar lo compte" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "I a pas cap de compte actiu" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gerir los comptes" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Marrit senhal" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Lo certificat TLS es invalid" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Volètz suprimir lo compte %s ?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Suprimir" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Seleccionar l’avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imatges" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Totes los fichièrs" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Connectat" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconnectat" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Error" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Ajustar un compte" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Lo servidor a pas pogut provar qu’es %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Vòstre sistèma operatiu se fisa pas del certificat prepausat." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Son certificat de seguretat foguèt provesit per un autre domeni." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Son certificat de seguretat serà pas que valid dins lo futur." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Son certificat a expirat." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Se connectar a %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Ara podètz utilizar lo compte %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Marrit nom d’utilizaire o senhal" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Quicòm a trucat" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Cap de responsa del servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Se marcar sus %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Lo servidor requesís una inscripcion via lo site web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Dobrir lo site web" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Se marcar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Consultatz %s per mai d’informacions tocant cossí se marchar" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Dobrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Enregistrar jos…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i son a escriure…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s son a escriure…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s escrivon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s escriu…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Sonada començada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Començat fa %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Avètz pres aquesta sonada d’un autre aparelh estant" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Sonada acabda" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Acabat a %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Durada %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Sonada mancada" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Avètz mancat aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s a mancat una sonada" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Sonada regetada" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Avètz regetada aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s a regetada aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Sonada fracassada" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ora" msgstr[1] "%i oras" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuta" msgstr[1] "%i minutas" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "qualques segondas" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Aqueste contacte volriá vos ajustar a sa lista de contactes" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Telecargament %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s a ofrit : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fichièr ofrit : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fichièr ofrit" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Fracàs del transferiment de fichièr" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fichièr" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar lo messatge" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Avètz pas cap conversacion dobèrta" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Escais-nom" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Ajustar un contacte" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "M’avisar quand un nòu messatge arriba" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Convertir los « smileys » en emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Verificar l’ortografia" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Client XMPP modèrn" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de " "provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte " "vòstra confidencialitat." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de " "configurar de foncionalitats ligadas amb la confidencialitat coma los " "acusats de lectura e las notificacions d’escritura." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres " "periferics." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Cap de recèrca activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Començatz d’escriure per aviar una recèrca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Cap de messatge correspondent" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verificatz l’ortografia o ensajatz de suprimir de filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "General" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Discussion" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegacion" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Sautar la discussion seguenta" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Sautar a la discussion precedenta" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Comptes" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Cap de compte pas configurat" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Ajustar un compte" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "S’identificar" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Crear un compte" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Establiment impossible d’una connexion segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Connexion" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Causissètz un servidor public" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "O indicatz l’adreça d’un servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Connectatz-vos allòc" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Causissètz un autre servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tot es prèst !" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Terminar" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clicatz aquí per començar una conversacion o jónher una sala." #~ msgid "No active conversations" #~ msgstr "Cap de discussion activa" #~ msgid "Main window with conversations" #~ msgstr "Fenèstra màger amb conversacions" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i autres" #~ msgid "You can now start using %s" #~ msgstr "Podètz ara començar d’utilizar %s" #~ msgid "Open Registration" #~ msgstr "Inscripcion dubèrtas" #~ msgid "Save" #~ msgstr "Enregistrar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "es a escriure…" #~ msgstr[1] "son a escriure…" #~ msgid "has stopped typing" #~ msgstr "a quitat d‘escriure" #~ msgid "%i search results" #~ msgstr "%i resultats de recèrca" #~ msgid "Discover real JIDs" #~ msgstr "Descobèrta dels JID reals" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qual pòt descobrir los JID reals ?" #~ msgid "Password required for room entry, if any" #~ msgstr "Senhal requesit per dintrar a la sala, se cal" #~ msgid "Failed connecting to %s" #~ msgstr "Fracàs de la connexion a %s" #~ msgid "Join Conference" #~ msgstr "Rejónher la conferéncia" dino-0.4.3/main/po/pl.po0000644000000000000000000011345414452563620013501 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-19 13:58+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Plik przekracza maksymalny rozmiar wysyłanych plików dla tego serwera." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Wiadomość zbyt długa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "edytowane" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "wysyłanie…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "wysyłanie nie powiodło się" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Bez szyfrowania" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Nie można wysłać wiadomości" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuta temu" msgstr[1] "%i minuty temu" msgstr[2] "%i minut temu" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Przed chwilą" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ja" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Wysłano obraz" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Wysłano plik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Odebrano obraz" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Odebrano plik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Połączenie wychodzące" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Połączenie przychodzące" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Wczoraj" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Rozpocznij rozmowę" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Właściciel" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Członek" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Użytkownik" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Zaproś" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Rozpocznij prywatną rozmowę" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Wyrzuć" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Udziel pozwolenia na pisanie" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Odwołaj pozwolenie na pisanie" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Zaproś do konferencji" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Wybierz plik" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Wybierz" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anuluj" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s z %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ta konferencja nie pozwala na wysyłanie wiadomości." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Prośba o dostęp" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Wyślij plik" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Rozpocznij połączenie" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Połączenie głosowe" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Połączenie wideo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Wyszukaj wiadomości" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Członkowie" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informacje debugowania" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Łączenie…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Łączenie…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Łączenie…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s zakończył połączenie" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s odrzucił połączenie" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamery" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nie znaleziono kamer." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofony" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nie znaleziono mikrofonów." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Głośniki" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nie znaleziono głośników." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Zaproś do Rozmowy" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Rozpocznij" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Dołącz do kanału" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Dalej" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Dołącz" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Wstecz" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Dołączanie…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Wstęp do pokoju wymaga hasła" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Dołączanie lub założenie konferencji zostało zablokowane" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Pokój nie istnieje" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Utworzenie pokoju niedozwolone" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Pokój tylko dla członków" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wybierz inny nick" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Zbyt wiele osób w pokoju" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Nie udało się połączyć z %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Niepoprawny adres" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Dodaj" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Skróty klawiszowe" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "O Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Dzisiaj" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i wynik wyszukiwania" msgstr[1] "%i wyniki wyszukiwania" msgstr[2] "%i wyników wyszukiwania" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "W %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Z %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Przychodzące połączenie wideo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Przychodząca grupowa rozmowa wideo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Przychodząca rozmowa grupowa" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Odrzuć" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akceptuj" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Prośba o subskrypcję" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Odmów" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Zaproszenie do %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s zaprosił cię do %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Prośba o dostęp" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s prosi o pozwolenie na pisanie na czacie z %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Ustawienia" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Ustawienia lokalne" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Wyślij powiadomienie o pisaniu" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Wyślij potwierdzenia odczytu" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Powiadomienia" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Włączone" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Wyłączone" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Tylko, gdy wspomniano nick" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Domyślnie: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Prośba" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Pozwolenia" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Poproś o pozwolenie na wysyłanie wiadomości" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Szczegóły konferencji" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Szczegóły kontaktu" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Zablokuj" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Blokowanie wysyłania statusu oraz wiadomości w obu kierunkach" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nazwa pokoju" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Opis pokoju" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Trwały" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Pokój nie zostanie usunięty po opuszczeniu przez ostatniego członka" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Publicznie wyszukiwalny" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Zmiana tematu możliwa przez użytkowników" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Zezwolenie na przeglądanie JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kto może wyświetlać identyfikatory JID użytkowników?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Hasło" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Hasło ograniczające dostęp do pokoju" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderowany" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Tylko użytkownicy z prawem głosu mogą wysyłać wiadomości" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Tylko dla członków" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Tylko członkowie mogą dołączyć do pokoju" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historia wiadomości" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maksymalna liczba przechowywanych wiadomości" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Konfiguracja pokoju" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Witaj w Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Zaloguj się lub załóż konto, aby rozpocząć." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Załóż konto" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Brak aktywnych kont" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Zarządzaj kontami" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Błędne hasło" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Nieprawidłowy certyfikat TLS" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Usunąć konto %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Usuń" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Wybierz awatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Obrazy" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Wszystkie pliki" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Połączony" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Rozłączony" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Błąd" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Dodaj konto" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Serwer nie mógł udowodnić że jest %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Jego certyfikat bezpieczeństwa nie jest zaufany w systemie operacyjnym tego " "komputera." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Jego certyfikat bezpieczeństwa został wystawiony dla innej domeny." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Jego certyfikat bezpieczeństwa nie jest jeszcze ważny." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Jego certyfikat bezpieczeństwa utracił ważność." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Zaloguj się do %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Możesz teraz użyć konta %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Niepoprawna nazwa użytkownika lub hasło" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Coś się nie powiodło" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Brak odpowiedzi z serwera" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Zarejestruj się na %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Serwer wymaga rejestracji przez stronę internetową" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Otwórz stronę internetową" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Zarejestruj się" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Dowiedz się na %s, jak się zarejestrować" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Otwórz" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Zapisz jako…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s i %i innych piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s i %s piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s i %s piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s pisze…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Połączenie rozpoczęte" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Połączenie rozpoczęte %s temu" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "To połączenie zostało odebrane na innym urządzeniu" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Połączenie zakończone" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Zakończone o %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Trwało %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Połączenie nieodebrane" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Nie odebrałeś tego połączenia" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s nie odebrał tego połączenia" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Połączenie odrzucone" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Odrzuciłeś to połączenie" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s odrzucił to połączenie" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Łączenie nie powiodło się" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "godzinę" msgstr[1] "%i godziny" msgstr[2] "%i godzin" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "minutę" msgstr[1] "%i minuty" msgstr[2] "%i minut" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "kilka sekund temu" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Ta osoba chciałaby Cię dodać do swoich kontaktów" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Pobieranie %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "oferowany %s: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Oferowany plik: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Oferta pliku" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Przesyłanie pliku nie powiodło się" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Plik" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aktualizuj wiadomość" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nie masz otwartych czatów" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Pseudonim" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonim" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Dodaj kontakt" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Powiadom o przychodzącej wiadomości" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Zamieniaj tekst na emotikony" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Sprawdź pisownie" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Nowoczesny komunikator XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej " "obsłudze sieci Jabber/XMPP dbając o twoją prywatność." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także " "daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o " "pisaniu czy odczytaniu wiadomości." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi " "urządzeniami." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Brak aktywnego wyszukiwania" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Zacznij pisać, aby wyszukać" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Brak pasujących wiadomości" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Sprawdź pisownię lub spróbuj usunąć filtry" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Wyślij" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Ogólne" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Rozmowa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Nawigacja" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Przejdź do następnej rozmowy" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Przejdź do poprzedniej rozmowy" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Konta" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokalny nick" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Brak skonfigurowanych kont" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Dodaj konto" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Zaloguj się" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Załóż konto" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Nie udało się nawiązać bezpiecznego połączenia" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Połącz" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Wybierz serwer publiczny" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Lub wprowadź adres serwera" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Przejdź do logowania" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Wybierz inny serwer" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Wszystko gotowe!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Zakończ" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Kliknij tutaj, aby rozpocząć rozmowę albo dołączyć do kanału." #~ msgid "No active conversations" #~ msgstr "Brak aktywnych rozmów" #~ msgid "Main window with conversations" #~ msgstr "Główne okno rozmów" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s i %i innych" #~ msgid "You can now start using %s" #~ msgstr "Możesz teraz używać konto %s" #~ msgid "Open Registration" #~ msgstr "Otwórz stronę" #~ msgid "Save" #~ msgstr "Zapisz" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s i %s" #~ msgid "%s and %s" #~ msgstr "%s i %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "pisze…" #~ msgstr[1] "piszą…" #~ msgstr[2] "piszą…" #~ msgid "has stopped typing" #~ msgstr "przestał pisać" #~ msgid "%i search results" #~ msgstr "%i wyniki wyszukiwania" #~ msgid "Discover real JIDs" #~ msgstr "Uprawnienie do wyświetlenia JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kto może widzieć JID użytkowników?" #~ msgid "Password required for room entry, if any" #~ msgstr "Hasło, aby ograniczyć dostęp do pokoju" #~ msgid "Failed connecting to %s" #~ msgstr "Nie udało się połączyć z %s" #~ msgid "Join Conference" #~ msgstr "Dołącz do konferencji" #~ msgid "Preferences" #~ msgstr "Preferencje" #~ msgid "Quit" #~ msgstr "Wyjdź" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID powinien być w formie \"użytkownik@przykład.com\"" #~ msgid "Copy Link Address" #~ msgstr "Skopiuj adres odnośnika" #~ msgid "Copy" #~ msgstr "Kopiuj" #~ msgid "Select All" #~ msgstr "Wybierz wszystkie" #~ msgid "Search" #~ msgstr "Wyszukaj" #~ msgid "Send message marker" #~ msgstr "Wyślij informację o odczytaniu wiadomości" #~ msgid "Start Chat" #~ msgstr "Rozpocznij rozmowę" dino-0.4.3/main/po/pt.po0000644000000000000000000010733214452563620013507 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-06-10 17:16+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.13-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "O ficheiro excede o tamanho máximo de envios do servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mensagem muito longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "Pendente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "Entrega falhou" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Não-criptografado" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Não foi possível enviar a mensagem" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min atrás" msgstr[1] "%i mins atrás" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Agora há pouco" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagem enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Ficheiro enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagem recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Ficheiro recebido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Chamada de Saída" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Chamada Recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ontem" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Iniciar conversa" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizador" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Banir" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permissão de escrita" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar permissão de escrita" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar para Conferência" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Selecionar ficheiro" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Selecionar" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Essa conferência não permite que envie mensagens." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Pedir permissão" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Enviar um ficheiro" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Fazer chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de Voz" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de Vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Procurar mensagens" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membros" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Depuração Informação" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "A Chamar…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "A Tocar…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conectando…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s Terminou a Chamada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s Rejeitou a Chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Câmera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Câmera não encontrada." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfone" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Microfone não encontrado." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Autofalantes" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Sem Autofalantes." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar para chamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Juntar-se a um canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Próximo" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Juntar-se" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Voltar" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Juntando-se…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Palavra-passe necessária para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Proibido de entrar ou criar uma conferência" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Sala não existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Não permitido criar sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala somente para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolha um apelido diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Quantidade máxima da sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Não foi possível se conectar a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Endereço inválido" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adicionar" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Atalhos de Teclado" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Sobre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoje" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da pesquisa" msgstr[1] "%i resultados da pesquisa" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Em %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Com %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "A receber Chamada de Vídeo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "A receber Chamada de Vídeo de Grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "A Receber Chamada de Grupo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceitar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Pedido de assinatura" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Negar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Convite para %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s convidou te para %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Pedido de permissão" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s está pedindo permissão para escrever em %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Configurações" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Configurações locais" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Enviar notificação ao digitar" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Enviar confirmação de leitura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificações" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Ligado" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Desligado" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Somente quando mencionado" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Predefinição: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Pedir" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permissões" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Pedir permissão para enviar mensagens" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalhes da Conferência" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhes do contato" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloquear" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Comunicação e atualizações de estado dos dois lados estão bloqueadas" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrição da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistente" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "A sala vai continuar depois do último integrante sair" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Publicamente pesquisável" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Integrantes podem mudar o assunto" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permissão de visualizar JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quem consegue visualizar os JIDs dos utilizadores?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Palavra-passe" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Uma palavra-passe para restringir o acesso à sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderado" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Somente integrantes com voz podem enviar mensagens" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Somente membros" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Somente membros podem entrar na sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Histórico de mensagens" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Quantidade máxima de histórico gerado pela sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuração da sala" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Seja bem vindo ao Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Entre ou crie uma conta para começar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurar a conta" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Nenhuma conta ativa" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gerir contas" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Palavra-passe incorreta" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Remover conta %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Remover" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Escolher avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imagens" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Todos os ficheiros" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conectado" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconectado" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Erro" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Adicionar conta" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor não conseguiu provar ser %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O seu certificado securitário não é confiável para o seu sistema operacional." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "O seu certificado securitário é emitido para outro domínio." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "O seu certificado securitário só se tornará válido no futuro." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "O seu certificado securitário está expirado." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Registar-se em %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Agora pode usar a conta %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nome ou Palavra-passe errada" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Algo deu errado" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Sem resposta do servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registe-se em %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "O servidor requer que se inscreva através de um site" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Abrir website" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Verifique %s para informação de como se inscrever" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Guardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i outros estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s está digitando…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Chamada Iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "%s decorridos" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Atendeu esta Chamada em outro aparelho" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Chamada terminada" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Terminada aos %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Durou %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Chamada Perdida" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Perdeu esta Chamada" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Chamada Recusada" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Recusou esta Chamada" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s recusou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Chamada falhada" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i Hora" msgstr[1] "%i Horas" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "alguns segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Este contato gostaria de adicioná-lo à sua lista de contatos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "A descarregar %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofereceu: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Ficheiro oferecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Ficheiro oferecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transferência de ficheiro falhou" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atualizar mensagem" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Não tem conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Apelido" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudônimo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adicionar contato" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notificar quando uma nova mensagem chegar" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Converter smileys para emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Verificar ortografia" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Moderno cliente de chat XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é um moderno chat de código aberto para desktop. Ele é focado em prover " "uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua " "privacidade." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar " "privacidade—características relacionadas às notificações de leitura, " "recebimento e escrita." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtém o histórico do servidor e sincroniza mensagens com outros " "aparelhos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sem busca ativa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escreva para iniciar a busca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sem mensagens correspondentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verifique a grafia ou tente remover filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Geral" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegação" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ir para a próxima conversa" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ir para a conversa anterior" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Contas" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Pseudônimo local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nenhuma conta configurada" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Adicionar conta" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Registar" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Criar conta" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Não foi possível estabelecer uma conexão segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Conectar" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Escolha um servidor publico" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ou especifique um endereço de servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Entrar em vez disso" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Escolha outro servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tudo configurado!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finalizado" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clique aqui para iniciar uma conversa or entrar num canal." dino-0.4.3/main/po/pt_BR.po0000644000000000000000000011264714452563620014077 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-15 23:02+0000\n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "O arquivo ultrapassa o máximo do tamanho de upload." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mensagem muito longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sem Criptografia" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Não foi possível enviar a mensagem" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d de %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d de %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min atrás" msgstr[1] "%i min atrás" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Agora há pouco" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagem enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Arquivo enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagem recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Arquivo recebido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Chamada realizada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Chamada recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ontem" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Iniciar Conversa" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuário" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permissão de escrita" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar permissão de escrita" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar para Conferência" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Selecionar arquivo" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Selecionar" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Essa conferência não permite que você envie mensagens." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Pedir permissão" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Enviar um arquivo" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de áudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Buscar mensagens" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membros" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informações de depuração" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Chamada em andamento…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Chamando…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conectando…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s terminou a chamada" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s recusou a chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Câmeras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nenhuma câmera encontrada." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfones" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nenhum microfone encontrado." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Juntar-se a um Canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Próximo" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Juntar-se" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Voltar" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Juntando-se…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Senha necessária para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Proibido de entrar ou criar uma conferência" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Sala não existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Sem permissão de criar sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala somente para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolha um apelido diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Capacidade de usuários na sala excedida" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Não foi possível se conectar a %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Endereço inválido" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adicionar" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Atalhos de Teclado" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Sobre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoje" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da pesquisa" msgstr[1] "%i resultados da pesquisa" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Em %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Com %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceitar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Pedido de assinatura" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Negar" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Convite para %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s te convidou para %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Pedido de permissão" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s está pedindo permissão para escrever em %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Configurações" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Configurações Locais" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Enviar notificação ao digitar" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Enviar confirmação de leitura" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificações" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Ligado" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Desligado" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Somente quando mencionado" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Padrão: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Pedir" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permissões" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Pedir permissão para enviar mensagens" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalhes da Conferência" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhes do Contato" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bloquear" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Comunicação e atualizações de status dos dois lados estão bloqueadas" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrição da sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistente" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "A sala vai continuar existindo mesmo que o último integrante saia" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Publicamente pesquisável" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Integrantes podem mudar o assunto" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permissão de visualizar JIDs" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quem consegue visualizar os JIDs dos usuários?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Senha" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Uma senha para restringir o acesso à sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderado" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Somente integrantes com voz podem enviar mensagens" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Somente membros" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Somente membros podem entrar na sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Histórico de mensagens" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Quantidade máxima de histórico armazenado na sala" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configuração da Sala" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Bem vindo ao Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Entre ou crie uma conta para começar." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurar a conta" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Nenhuma conta ativa" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Gerenciar contas" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Senha incorreta" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Remover conta %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Remover" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Escolher avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imagens" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Todos os arquivos" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conectado" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Desconectado" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Erro" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Adicionar Conta" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor contactado não conseguiu provar que representa %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O certificado de segurança do servidor não é considerado confiável por seu " "sistema operacional." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Seu certificado de segurança for emitido para outro domínio." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Seu certificado de segurança se tornará válido apenas no futuro." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Seu certificado de segurança está expirado." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Entre em %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Você agora pode usar a conta %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nome ou senha incorretos" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Algo deu errado" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Sem resposta do servidor" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registre-se em %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "O servidor requer registro através de um site" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Abrir website" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrar" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Verifique %s para informação de como se registrar" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salvar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i outros estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s está digitando…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Chamada iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Chamada terminada" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Este contato gostaria de adicioná-lo à sua lista de contatos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Baixando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofereceu: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Arquivo oferecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Arquivo oferecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transferência de arquivo falhou" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Arquivo" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atualizar mensagem" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Você não tem conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Apelido" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudônimo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adicionar Contato" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notificar quando uma nova mensagem chegar" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Converter smileys para emojis" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Verificar ortografia" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Cliente de Chat XMPP Moderno" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em " "oferecer uma experiência Jabber/XMPP transparente e confiável levando em " "consideração a sua privacidade." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, " "permite configurar funcionalidades relativas à privacidade como notificações " "de leitura, recebimento e escrita." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtém o histórico do servidor e sincroniza mensagens com outros " "dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sem busca ativa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escreva para iniciar uma busca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sem mensagens correspondentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verifique a grafia ou tente remover filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Geral" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Conversa" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navegação" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Ir para a próxima conversa" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Ir para a conversa anterior" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Contas" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Pseudônimo local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nenhuma conta configurada" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Adicionar uma conta" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Entrar" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Criar conta" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Não foi possível estabelecer uma conexão segura" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Conectar" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Escolha um servidor público" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ou especifique um endereço de servidor" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Entrar em vez disso" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Escolher outro servidor" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Tudo configurado!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finalizado" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clique aqui para inicial uma conversa ou entrar em um canal." #~ msgid "No active conversations" #~ msgstr "Nenhuma conversa ativa" #~ msgid "Main window with conversations" #~ msgstr "Janela principal com as conversas" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e outros %i" #~ msgid "You can now start using %s" #~ msgstr "Agora você pode começar a usar %s" #~ msgid "Open Registration" #~ msgstr "Abrir Site de Inscrição" #~ msgid "Save" #~ msgstr "Salvar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está digitando…" #~ msgstr[1] "estão digitando…" #~ msgid "has stopped typing" #~ msgstr "parou de digitar" #~ msgid "%i search results" #~ msgstr "%i resultado(s) da busca" #~ msgid "Discover real JIDs" #~ msgstr "Descobrir JIDs reais" #~ msgid "Who may discover real JIDs?" #~ msgstr "Quem pode encontrar JIDs reais?" #~ msgid "Password required for room entry, if any" #~ msgstr "Senha requerida para entrar na sala, se houver" #~ msgid "Failed connecting to %s" #~ msgstr "Falha ao se conectar com %s" #~ msgid "Join Conference" #~ msgstr "Juntar-se a uma conferência" #~ msgid "Communicate happiness." #~ msgstr "Comunique-se alegremente." #~ msgid "Preferences" #~ msgstr "Preferências" #~ msgid "Quit" #~ msgstr "Sair" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "O Jabber ID deve estar no formato \"usuário@exemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar endereço do link" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Selecionar tudo" #~ msgid "Search" #~ msgstr "Pesquisar" #~ msgid "Send message marker" #~ msgstr "Enviar marcador de mensagem" #~ msgid "Start Chat" #~ msgstr "Iniciar bate-papo" #~ msgid "Request presence updates" #~ msgstr "Pedir atualizações sobre presença" #~ msgid "Join on startup" #~ msgstr "Conectar-se ao inicializar" #~ msgid "Add Chat" #~ msgstr "Adicionar bate-papo" dino-0.4.3/main/po/ro.po0000644000000000000000000011424014452563620013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Fișierul depășește dimensiunea maximă de încărcare a serverului." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mesaj prea lung" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "editat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "în așteptare…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "livrare eșuată" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Necriptat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Nu se poate transmite mesajul" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Acum %i minut" msgstr[1] "Acum %i minute" msgstr[2] "Acum %i de minute" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Chiar acum" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagine trimisă" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fișier trimis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagine primitä" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fișier primit" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Apel efectuat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Apel primit" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ieri" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Pornește o conversație" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietar" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membru/ă" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizator" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invită" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Pornește o conversație privată" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Dă afară" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Acordă permisiunea de a scrie" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca permisiunea de a scrie" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invită la o conferință" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Selectare fișier" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Selectare" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anulare" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s de la %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Această conferință nu vă permite să trimiteți mesaje." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicitare permisiune" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Trimite fișier" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Începe un apel" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Apel audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Apel video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Caută mesaje" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membri" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Informații de depanare" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Se apelează…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Sună…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Conectare…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s a încheiat apelul" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s a refuzat apelul" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camere" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nici o cameră găsită." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoane" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nici un microfon găsit." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Difuzoare" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nici un difuzor găsit." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invitați la apel" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Pornire" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Alăturați-vă canalului" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Următorul" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Alăturați-vă" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Înapoi" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Conectare…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Este necesară o parolă pentru a te putea alătura acestei discuții" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Aveți interdicție de a vă alătura sau de a crea o conferință" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Această discuție nu există" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Este interzisă crearea unei discuții" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Discuție accesibilă numai membrilor" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Alegeți un nume diferit" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Discuția are prea mulți membrii" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Nu s-a putut face conectarea la %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Adresă invalidă" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adaugă" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Scurtături de tastatură" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Despre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Azi" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i rezultat de căutare" msgstr[1] "%i rezultate de căutare" msgstr[2] "%i de rezultate de căutare" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "În %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Cu %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Apel video primit" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Apel de grup video primit" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Apelul de grup primit" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Respinge" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptă" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Cerere de abonare" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Refuză" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Invitaţie la %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s v-a invitat la %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Solicitare de permisiune" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s solicită permisiunea de a scrie în %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Setări" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Setări locale" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Trimite notificare la tastare" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Trimite notificare la primire" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Notificări" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Fixează discuția" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Fixează discuția în partea de sus a listei de discuții" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Da" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Nu" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Doar la mențiuni" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Implicit: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Cerere" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Permisiuni" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Solicita permisiunea de a trimite mesaje" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Detalii conferință" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalii contact" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blochează" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Comunicațiile și actualizările de stare sunt blocate în ambele sensuri" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Numele discuției" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Descrierea discuției" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Persistentă" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Discuția va continua să existe și după ce ultima persoană va ieși" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Vizibilă public" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Membrii pot schimba subiectul" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Permisiunea de a vizualiza JID-uri" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Cine are voie să vadă JID-urile ocupanților?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Parolă" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "O parolă pentru a restricționa accesul la discuție" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderată" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Doar membrii cu drept de voce vor putea trimite mesaje" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Doar pentru membri" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Numai membrii se pot alătura discuției" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Istoric mesaje" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Numărul maxim de mesaje din istoric ce va fi afișat în discuție" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Configurare discuție" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Bine ați venit la Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Autentificați-vă sau creați un cont pentru a începe." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Configurare cont" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Nici un cont activ" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Administrare conturi" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Parolă greșită" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Certificat TLS invalid" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Se șterge contul %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Șterge" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Selectare avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Imagini" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Toate fișierele" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Conectat" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Deconectat" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Eroare" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Adaugă un cont" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Serverul nu a putut dovedi că este %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Sistemul dumneavoastră de operare nu are încredere în certificatul său de " "securitate." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Certificatul său de securitate este emis pentru alt domeniu." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Certificatul său de securitate va deveni valabil numai în viitor." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Certificatul său de securitate este expirat." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Autentificare în %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Acum puteți începe să utilizați contul %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Nume utilizator sau parolă greșite" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "A apărut o problemă" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Nici un răspuns de la server" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Înregistrare pe %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Serverul necesită înregistrarea printr-un site web" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Deschide site-ul" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Înregistrează-te" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Aflați pe %s informații despre cum se face înregistrarea" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Editare mesaj" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tu" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Adaugă reacție" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Deschide" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salvează ca…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s și încă %i tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s și %s tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s și %s tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s tastează…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Apelul a început" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "A început acum %s" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Ați recepționat acest apel pe un alt dispozitiv" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Apel încheiat" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "S-a încheiat la %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "A durat %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Apel ratat" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Ați pierdut acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s a pierdut acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Apel refuzat" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Ați refuzat acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s a refuzat acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Apelul a eșuat" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i oră" msgstr[1] "%i ore" msgstr[2] "%i de ore" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minute" msgstr[2] "%i de minut" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "câteva secunde" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Acest contact dorește să vă adauge la lista ei/lui de contacte" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Răspunde" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Se descarcă %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s a oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fișier oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fișier oferit" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Transferul fișierului a eșuat" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fișier" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizare mesaj" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nu aveți nici o discuție deschisă" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Apăsați + pentru a porni o discuție sau să vă alăturați unui grup" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Cont" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nume" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adaugă contact" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Notifică atunci când este primit un mesaj nou" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Convertește zâmbilici în emoticoane" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Verificare ortografie" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Client XMPP de discuții modern" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. " "Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, " "ținând cont de confidențialitatea dumneavoastră." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, " "și permite configurarea caracteristicilor legate de confidențialitate precum " "trimiterea notificărilor de primire și tastare." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu " "celelalte dispozitive." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nici o căutare activă" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tastați ceva pentru a porni căutarea" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nici un mesaj nu se potrivește" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verificați ortografia sau încercați să eliminați filtre" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Trimite" #: main/data/shortcuts.ui:10 msgid "General" msgstr "General" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Discuție" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigare" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Salt la următoarea conversație" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Salt la conversația anterioară" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Conturi" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias local" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Nici un cont configurat" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Adaugă un cont" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Autentificare" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Creează cont" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Nu s-a putut stabili o conexiune securizată" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Conectare" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Alegeți un server public" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Sau specificați adresa serverului" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Înapoi la conectare" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Alege alt server" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Gata!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Finalizare" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Apăsați aici pentru a porni o conversație sau a vă alătura unui canal." #~ msgid "No active conversations" #~ msgstr "Nici o conversație activă" #~ msgid "Main window with conversations" #~ msgstr "Fereastra principală de conversații" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s și încă %i alții" #~ msgid "You can now start using %s" #~ msgstr "Acum puteți utiliza %s" #~ msgid "Open Registration" #~ msgstr "Deschis pentru înregistrare" #~ msgid "Save" #~ msgstr "Salvare" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s și %s" #~ msgid "%s and %s" #~ msgstr "%s și %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tastează…" #~ msgstr[1] "tastează…" #~ msgstr[2] "tastează…" #~ msgid "has stopped typing" #~ msgstr "s-a oprit din scris" #~ msgid "%i search results" #~ msgstr "%i rezultate la căutare" #~ msgid "Discover real JIDs" #~ msgstr "Descoperă adresele adevărate" #~ msgid "Who may discover real JIDs?" #~ msgstr "Cine poate descoperi adresele adevărate?" #~ msgid "Password required for room entry, if any" #~ msgstr "Dacă există, o parolă va fi necesară pentru alăturarea la discuție" #~ msgid "Failed connecting to %s" #~ msgstr "Eroare la conectarea cu %s" #~ msgid "Join Conference" #~ msgstr "Alătură-te unei conferințe" #~ msgid "Preferences" #~ msgstr "Preferințe" #~ msgid "Quit" #~ msgstr "Ieșire" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID trebuie să fie de forma “utilizator@exemplu.ro”" #~ msgid "Copy Link Address" #~ msgstr "Copiază adresa legăturii" #~ msgid "Copy" #~ msgstr "Copiere" #~ msgid "Select All" #~ msgstr "Selectează tot" #~ msgid "Search" #~ msgstr "Căutare" #~ msgid "Send message marker" #~ msgstr "Trimite marcajul mesajului" #~ msgid "Start Chat" #~ msgstr "Pornește o discuție" dino-0.4.3/main/po/ru.po0000644000000000000000000012375514452563620013521 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Размер файла превышает максимальный размер загрузки сервера." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Сообщение слишком длинное" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "ред." #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "ожидание…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "доставка не удалась" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Не зашифровано" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Не удаётся отправить сообщение" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%H∶%M, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%l∶%M, %x %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i минута назад" msgstr[1] "%i минуты назад" msgstr[2] "%i минут назад" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Только что" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Я" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Изображение отправлено" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Файл отправлен" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Изображение получено" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Файл получен" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Исходящий звонок" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Входящий звонок" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Вчера" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Начать чат" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Владелец" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Администратор" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Участник" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Пользователь" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Пригласить" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Начать личный чат" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Выгнать" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Выдать разрешение писать" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Отобрать разрешение писать" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Пригласить в чат" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Выберите файл" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Выбрать" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Отмена" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s из %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Вам запретили отправлять сообщения в этой беседе." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Запросить разрешение" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Прикрепить файл" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Начать звонок" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Аудиозвонок" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Видеозвонок" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Поиск сообщений" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Участники" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Отладочная информация" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Звонок…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Звонок…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Подключение…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s завершил(а) звонок" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s отклонил(а) звонок" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Камеры" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Камера не найдена." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Микрофоны" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Микрофон не найден." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Динамики" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Динамик не найден." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Пригласить на звонок" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Начать" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Войти в канал" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Далее" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Войти" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Назад" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Присоединение…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Требуется пароль для входа в комнату" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Вам запрещено заходить в конференции или создавать их" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Данной комнаты не существует" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Вам запрещено создавать комнаты" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Комната только для участников" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Выберите другой никнейм" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "В комнате слишком много посетителей" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Не удаётся подключиться к %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Недействительный адрес" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Добавить" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Горячие клавиши" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "О Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Сегодня" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i результат поиска" msgstr[1] "%i результата поиска" msgstr[2] "%i результатов поиска" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "В %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "С %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Входящий видеозвонок" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Входящий групповой видеозвонок" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Входящий групповой звонок" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Отклонить" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Принять" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Требуется авторизация" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Отклонить" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Приглашение в %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s пригласил вас в %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Запрос на разрешения" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s запросил(а) разрешение на возможность писать в %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Настройки" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Локальные настройки" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Отправлять уведомления при наборе сообщения" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Отправлять уведомления о прочтении" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Уведомления" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "ВКЛ" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "ВЫКЛ" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Только при упоминании" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "По умолчанию: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Запросить" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Разрешения" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Запросить разрешение на отправку сообщений" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Информация о конференции" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Информация о контакте" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Заблокировать" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Переписка и обновления статусов заблокированы в оба направления" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Название комнаты" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Описание комнаты" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Сохранить комнату пустой" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Комната сохранится, даже если уйдёт последний посетитель" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Доступность для публичного поиска" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Посетители могут менять тему чата" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Разрешение на просмотр JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Кому разрешено просматривать JID посетителей?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Пароль" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Пароль для ограничения доступа к комнате" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Модерируемый" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Только посетители с голосом(разрешение) могут отправлять сообщения" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Только для участников" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Только участники могут заходить в комнату" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "История сообщений" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Максимальное количество логов, оставляемое комнатой" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Настройки комнаты" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Добро пожаловать в Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Войдите или создайте учётную запись, чтобы начать использование." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Настроить учётную запись" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Нет активных учётных записей" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Управление учётными записями" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Неверный пароль" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Недействительный TSL сертификат" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Удалить аккаунт %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Удалить" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Выбрать аватарку" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Изображения" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Все файлы" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Подключено" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Отключено" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Ошибка" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Добавить аккаунт" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Сервер не смог доказать, что %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Его сертификат безопасности не доверяет вашей операционной системе." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Его сертификат безопасности выдается другому домену." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Его сертификат безопасности станет действительным только в будущем." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Срок действия его сертификата безопасности истек." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Вход в уч. запись %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Теперь вы можете использовать учётную запись %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Неправильный логин или пароль" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Что-то пошло не так" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Нет ответа от сервера" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Регистрация в %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Сервер требует пройти авторизацию через сайт" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Открыть веб-сайт" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Зарегистрироваться" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Прочтите %s, чтобы узнать о процессе авторизации" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Открыть" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Сохранить как…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s и ещё %i печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s и %s печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s и %s печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s печатает…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Звонок начат" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Начат %s назад" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Вы обработали этот звонок на другом устройстве" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Звонок завершен" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Завершен в %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Продлился %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Пропущенный звонок" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Вы пропустили этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s пропустил(а) этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Звонок отклонен" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Вы отклонили этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s отклонил(а) этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Звонок не удался" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i час" msgstr[1] "%i часа" msgstr[2] "%i часов" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i минута" msgstr[1] "%i минуты" msgstr[2] "%i минут" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "несколько секунд" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Этот контакт хочет добавить вас в свой список контактов" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Скачивание %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s. Приблизительный размер: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Приблизительный размер файла: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Предложен файл" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Ошибка передачи файла" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Файл" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Редактировать сообщение" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Тут пустовато" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Уч. запись" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Никнейм" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Псевдоним" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Добавить контакт" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Уведомлять о новых сообщениях" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Превращать смайлы в эмодзи" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Проверка орфографии" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Современный XMPP клиент" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino - это современный клиент для чатов с открытым исходным кодом, " "направленный на надёжное и приватное использование Jabber/XMPP на " "персональных компьютерах." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет " "настраивать такие функции, как уведомления о прочтении и наборе сообщений." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino загружает историю с сервера и синхронизирует сообщения с другими " "устройствами." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Поиск по сообщениям" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Начните писать то, что хотите найти" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Нет подходящих сообщений" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Проверьте словари или измените критерии поиска" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Отправить" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Основное" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Беседа" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Навигация" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Перейти к следующему чату" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Перейти к предыдущему чату" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Учётные записи" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Псевдоним" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Нет настроенных уч. записей" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Добавить аккаунт" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Войти" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Создать аккаунт" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Не удалось установить защищённое соединение" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Присоединиться" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Выберите публичный сервер" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Или введите адрес сервера сами" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Войти" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Выбрать другой сервер" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Всё готово!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Закончить" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Нажмите здесь, чтобы начать беседу или присоединиться к каналу." #~ msgid "No active conversations" #~ msgstr "Нет активных чатов" #~ msgid "Main window with conversations" #~ msgstr "Главное окно с чатами" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s и ещё %i человек" #~ msgid "You can now start using %s" #~ msgstr "Теперь вы можете использовать %s" #~ msgid "Open Registration" #~ msgstr "Открытая регистрация" #~ msgid "Save" #~ msgstr "Сохранить" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s и %s" #~ msgid "%s and %s" #~ msgstr "%s и %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "печатает…" #~ msgstr[1] "печатают…" #~ msgstr[2] "печатают…" #~ msgid "has stopped typing" #~ msgstr "перестал печатать" #~ msgid "%i search results" #~ msgstr "Результаты поиска по запросу «%i»" #~ msgid "Discover real JIDs" #~ msgstr "Показывать настоящие JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Кто может видеть настоящие JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "Требуется пароль для входа в комнату, если" #~ msgid "Failed connecting to %s" #~ msgstr "Не удалось соединение с %s" #~ msgid "Join Conference" #~ msgstr "Войти в конференцию" #~ msgid "Preferences" #~ msgstr "Опции" #~ msgid "Quit" #~ msgstr "Выйти" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID должен быть в виде «user@example.com»" #~ msgid "Copy Link Address" #~ msgstr "Копировать адресс ссылки" #~ msgid "Copy" #~ msgstr "Копировать" #~ msgid "Select All" #~ msgstr "Выбрать всё" #~ msgid "Search" #~ msgstr "Поиск" #~ msgid "Send message marker" #~ msgstr "Отправлять маркеры сообщений" #~ msgid "Start Chat" #~ msgstr "Начать чат" dino-0.4.3/main/po/sq.po0000644000000000000000000010761414452563620013512 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Kjo kartelë e tejkalon madhësinë maksimum për ngarkime të shërbyesit." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mesazh shumë i gjatë" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "përpunoi" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "pezull…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "dërgimi dështoi" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "I pafshehtëzuar" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "S’arrihet të dërgohet mesazh" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minutë më parë" msgstr[1] "%i minuta më parë" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Mu tani" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Unë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Figura u dërgua" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Kartela u dërgua" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "U mor figurë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "U mor kartelë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Thirrje e dërguar" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Thirrje e marrë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %B" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Dje" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Nisni Bisedë" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Pronar" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Përgjegjës" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Anëtar" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Përdorues" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Ftoje" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Nisni bisedë private" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Përzëre" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Akordojini leje shkrimi" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Shfuqizojini leje shkrimi" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Ftoni në Konferencë" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Përzgjidhni kartelë" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Përzgjidhe" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anuloje" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s prej %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Kjo konferencë s’ju lejon të dërgoni mesazhe." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Kërkesë për leje" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Dërgoni një kartelë" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Ndihmëz për butonin “nis thirrje”" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Thirrje me zë" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Thirrje me video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Kërko te mesazhet" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anëtarë" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Hollësi diagnostikimi" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Po thirret…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Po i bihet ziles…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Po lidhet…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s përfundoi thirrjen" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s s’pranoi thirrjen" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "S’u gjet kamera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonë" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "S’u gjet mikrofon." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altoparlantë" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "S’u gjetën altoparlantë." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Ftoni për Thirrje" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Fillo" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Hyni në Kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Pasuesi" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Merrni pjesë" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Mbrapsht" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Po hyhet…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lypset fjalëkalim për të hyrë në dhomë" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "I dëbuar nga pjesëmarrje apo krijim konference" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Dhoma s’ekziston" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "S’keni leje të krijoni dhomë" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Dhomë vetëm për anëtarë" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Zgjidhni nofkë tjetër" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Shumë të pranishëm në dhomë" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "S’u lidh dot te %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Adresë e pavlefshme" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Shto" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Shkurtore Tastiere" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Mbi Dino-s" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Sot" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i përfundim kërkimi" msgstr[1] "%i përfundime kërkimi" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "Në %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Me %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Thirrje ardhëse me video" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Thirrje ardhëse me video për grupin" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Thirrje ardhëse për grupin" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Hidhe tej" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Pranojeni" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Kërkesë pajtimi" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Mohojeni" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Ftesë për te %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s ju ftoi te %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Kërkesë për leje" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s kërkon leje për shkrim te %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Rregullime" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Rregullime Vendore" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Dërgo njoftime shtypjesh" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Dërgo dëftesa leximi" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Njoftime" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "On" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Off" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Vetëm kur përmendet" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Parazgjedhje: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Kërkesë" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Leje" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Kërko leje për dërgim mesazhesh" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Hollësi Konference" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Hollësi Kontakti" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Bllokoje" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" "Përditësimet mbi komunikimin dhe gjendjet, nga cilido kah, janë të bllokuara" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Emër i dhomës" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Përshkrim i dhomës" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "E qëndrueshme" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Dhoma do të mbetet, pasi të dalë i pranishmi i fundit" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Mund të kërkohet publikisht" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Të pranishmit mund të ndryshojnë subjektin" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Leje për të parë JID-ra" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Cilit i është lejuar të shohë JID pjesëmarrësish?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Fjalëkalim" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Një fjalëkalim për të kufizuar hyrje te dhoma" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "I moderuar" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Vetëm të pranishëm me zë mund të dërgojnë mesazhe" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Vetëm anëtarët" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Në dhomë mund të futen vetëm anëtarë" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Historik mesazhesh" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Sasi maksimum mesazhesh historiku të emetuar nga dhoma" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Formësim Dhome" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Mirë se vini te Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Që t’ia filloni, bëni hyrjen ose krijoni një llogari." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Ujdisni llogari" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Pa llogari aktive" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Administroni llogari" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Fjalëkalim i gabuar" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Dëshmi TLS e pavlefshme" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Të hiqet llogaria %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Hiqe" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Përzgjidhni avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Figura" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Krejt kartelat" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "U lidh" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "U shkëput" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Gabim" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Shtoni Llogari" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Shërbyesi s’provoi dot se është %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Dëshmia e tij e sigurisë nuk besohet nga sistemi juaj operativ." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Dëshmia e tij e sigurisë është emetuar nga tjetër përkatësi." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Dëshmia e tij e sigurisë do të bëhet e vlefshme vetëm në të ardhmen." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Dëshmia e tij e sigurisë ka skaduar." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Hyni te %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Tani mund të përdorni llogarinë %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Emër përdoruesi ose fjalëkalim i gabuar" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Diçka shkoi keq" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "S’ka përgjigje nga shërbyesi" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Regjistrohuni në %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Shërbyesi kërkon doemos të bëhet regjistrim përmes një sajti" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Hap sajtin" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Regjistrohuni" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Për hollësi se si të regjistroheni, shihni te %s" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Hape" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Ruajeni si…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s dhe %i të tjerë po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s dhe %s po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s, dhe %s po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s po shkruan…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Nisi thirrja" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Filluar %s më parë" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Me këtë thirrje u morët në pajisje tjetër" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Thirrja përfundoi" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Përfundoi më %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Zgjati %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Thirrje e humbur" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "E humbët këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s e humbi këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Thirrja s’u pranua" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "S’e pranuat këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s s’e pranoi këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Thirrja dështoi" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i orë" msgstr[1] "%i orë" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minutë" msgstr[1] "%i minuta" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "pak sekonda" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Ky kontakt do të donte t’ju shtonte te lista e tyre e kontakteve" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Po shkarkohet %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s ofroi: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "U ofrua kartelë: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "U ofrua kartelë" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Shpërngulja e kartelës dështoi" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Përditësoni mesazhin" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "S’keni fjalosje të hapura" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Llogari" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nofkë" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Shtoni Kontakt" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Njoftomë kur mbërrin mesazh i ri" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Shndërro emotikonet në emoji" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Kontroll drejtshkrimi" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Klient Modern Fjalosjesh XMPP" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai " "përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme " "për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim " "veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime " "shtypjeje." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje " "të tjera." #: main/data/global_search.ui:27 msgid "No active search" msgstr "S’ka kërkim aktiv" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Shtypni që të niset një kërkim" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "S’ka mesazhe me përputhje" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kontrolloni drejtshkrimin ose provoni të hiqni filtra" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Dërgoje" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Të përgjithshme" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Bisedë" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Lëvizje" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Hidhu te biseda pasuese" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Hidhu te biseda e mëparshme" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Llogari" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Alias vendor" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "S’ka llogari të formësuara" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Shtoni një llogari" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Hyni" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Krijoni llogari" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "S’u vendos dot një lidhje të sigurt" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Lidhuni" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Zgjidhni një shërbyes publik" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Ose specifikoni një adresë shërbyesi" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Hyni, më mirë" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Zgjidhni një tjetër shërbyes" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Gjithçka e ujdisur!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Përfundoje" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klikoni këtu që të nisni një bisedë ose të hyni në një kanal." dino-0.4.3/main/po/sv.po0000644000000000000000000011204214452563620013506 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-15 23:54+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Filen är större än vad servern tillåter." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Meddelandet är för långt" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "redigerad" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "väntar…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "leverans misslyckades" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Okrypterat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Kunde inte skicka meddelandet" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H : %M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minut sedan" msgstr[1] "%i minuter sedan" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Nyss" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Jag" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild skickad" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil skickad" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild mottagen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil mottagen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Utgående samtal" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Inkommande samtal" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Igår" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Starta Chatt" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Ägare" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Medlem" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Användare" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Bjud in" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Starta privat konversation" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kasta ut" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Ge skrivrättigheter" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Återkalla skrivrättigheter" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Bjud in till gruppchatt" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Välj fil" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Välj" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Avbryt" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s från %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Den här konferensen låter dig inte skicka meddelanden." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Be om tillstånd" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Skicka en fil" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Starta samtal" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Ljudsamtal" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videosamtal" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Sök meddelanden" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Medlemmar" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Felsökningsinformation" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Ringer…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Ringer…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Ansluter…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s avslutade samtalet" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s avvisade samtalet" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameror" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ingen kamera hittades." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofoner" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ingen mikrofon hittades." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Högtalare" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ingen högtalare hittades." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Bjud in till samtal" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Starta" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Gå med i kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Nästa" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Gå med" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Tillbaka" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Går med…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lösenord krävs för att gå med i rummet" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Förbjuden att gå med eller skapa gruppchattar" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Rummet finns inte" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Saknar tillåtelse att skapa rum" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Endast medlemmar tillåtna i rummet" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Välj ett annat smeknamn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "För många användare i rummet" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Kunde inte ansluta till %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Ogiltig adress" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Lägg till" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Snabbkommandon" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Om Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Idag" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i sökresultat" msgstr[1] "%i sökresultat" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "I %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "Med %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Inkommande videosamtal" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Inkommande videogruppsamtal" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Inkommande gruppsamtal" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Avslå" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Godkänn" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Prenumerationsförfrågan" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Neka" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Inbjudan till %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s bjöd in dig till %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Åtkomstbegäran" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ber om tillstånd att skriva i %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Inställningar" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Lokala inställningar" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "Skicka skriftaviseringar" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "Skicka läsbekräftelser" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Aviseringar" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "På" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Av" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Vid omnämnande" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "Fråga" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "Tillstånd" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Be om tillstånd att skicka meddelanden" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Konferensdetaljer" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetaljer" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Blockera" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "Kommunikation och statusuppdatering i båda riktningar blockeras" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Namn på rummet" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Beskrivning av rummet" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Behåll" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" "Rummet kommer att finnas kvar även efter att sista användaren har lämnat" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Offentligt sökbart" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Användare får ändra ämnet" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "Tillstånd att visa JID" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Vem har tillstånd att se användarnas JID?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Lösenord" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Det krävs ett lösenord för att få tillträde till rummet" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderering" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Endast användare med tillåtelse får skicka meddelanden" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Endast för medlemmar" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Endast medlemmar får ansluta till rummet" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Meddelandehistorik" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Maxgräns på meddelandehistoriken i rummet" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Rumskonfiguration" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Välkommen till Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Logga in eller skapa ett konto för att komma igång." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Ställ in konto" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Inga aktiva konton" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Hantera konton" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Fel lösenord" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Ogiltigt TLS-certifikat" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Ta bort konto %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Ta bort" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Välj avatar" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Bilder" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Alla filer" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Ansluten" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Frånkopplad" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Fel" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Lägg till konto" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Servern kunde inte bevisa att den är %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Ditt operativsystem litar inte på dess säkerhetscertifikat." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Dess säkerhetscertifikat är utgivet till en annan domän." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Dess säkerhetscertifikat blir giltigt i framtiden." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Dess säkerhetscertifikat har gått ut." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Logga in på %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Du kan nu börja använda kontot %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Fel användarnamn eller lösenord" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Något gick fel" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Inget svar från servern" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Registrera konto på %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Servern kräver registrering genom en webbsida" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Öppna webbsida" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Registrera" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Kolla %s för information om registrering" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Öppna" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Spara som…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s och %i andra skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s och %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s och %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s skriver…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Samtal startat" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "Startade för %s sedan" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Du hanterade detta samtal på en annan enhet" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Samtal avlutat" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "Avslutades %s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "Varade %s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Missat samtal" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Du svarade inte på det här samtalet" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s svarade inte på det här samtalet" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Avvisat samtal" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Du avvisade detta samtal" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s avvisade detta samtal" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Samtal misslyckades" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i timme" msgstr[1] "%i timmar" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minuter" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "några sekunder" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Denna kontakt vill lägga till dig i sin kontaktlista" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Tar emot %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s erbjöd: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Fil erbjuden: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Fil erbjuden" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Filöverföring misslyckades" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fil" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Uppdatera meddelande" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du har inga öppna konversationer" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Smeknamn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Lägg till kontakt" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Avisera när ett nytt meddelande mottages" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "Konvertera smileys till emojin" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Kontrollera stavning" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Modern XMPP-chattklient" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder " "en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser " "efter din integritet." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter " "konfigurering av funktioner med integritetspåverkan som läsbekräftelser och " "skriftaviseringar." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino hämtar historik från servern och synkroniserar meddelanden med andra " "enheter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ingen aktiv sökning" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skriv för att börja söka" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Inga meddelande matchade" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kolla stavningen eller ta bort filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Skicka" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Allmänt" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Konversation" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigering" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Hoppa till nästa konversation" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Hoppa till föregående konversation" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Konton" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Lokalt alias" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Inga konfigurerade konton" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Lägg till konto" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Logga in" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Skapa konto" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Kunde inte etablera en säker anslutning" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Anslut" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Välj en offentlig server" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Eller ange en serveraddress" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Logga in i stället" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Välj en annan server" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Färdigt!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Slutför" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klicka här för att starta en konversation eller gå med i en kanal." #~ msgid "Video call incoming" #~ msgstr "Inkommande videosamtal" #~ msgid "Call incoming" #~ msgstr "Inkommande samtal" #~ msgid "Establishing call" #~ msgstr "Upprättar samtal" #~ msgid "Video call establishing" #~ msgstr "Upprättar videosamtal" #~ msgid "Call establishing" #~ msgstr "Upprättar samtal" #~ msgid "Call in progress…" #~ msgstr "Samtal pågår…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Varade i %s" #~ msgid "seconds" #~ msgstr "några sekunder" #~ msgid "No active conversations" #~ msgstr "Inga aktiva chattar" #~ msgid "Main window with conversations" #~ msgstr "Huvudfönster med konversationer" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s och %i andra" #~ msgid "You can now start using %s" #~ msgstr "Du kan nu börja använda %s" #~ msgid "Open Registration" #~ msgstr "Öppna Registreringen" #~ msgid "Save" #~ msgstr "Spara" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s och %s" #~ msgid "%s and %s" #~ msgstr "%s och %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "skriver…" #~ msgstr[1] "skriver…" #~ msgid "has stopped typing" #~ msgstr "har slutat skriva" #~ msgid "%i search results" #~ msgstr "%i sökresultat" #~ msgid "Discover real JIDs" #~ msgstr "Upptäckt riktiga JID:n" #~ msgid "Who may discover real JIDs?" #~ msgstr "Vem får upptäcka riktiga JID:n?" #~ msgid "Password required for room entry, if any" #~ msgstr "Lösenord som krävs för tillträde, om något" #~ msgid "Failed connecting to %s" #~ msgstr "Anslutning till %s misslyckades" #~ msgid "Join Conference" #~ msgstr "Anslut till gruppchatt" dino-0.4.3/main/po/ta.po0000644000000000000000000007571214452563620013476 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-16 20:11+0000\n" "Language-Team: none\n" "Language: ta\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0.1-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "நேற்று" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "இன்று" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, %s தட்டச்சிடுகின்றனர்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s, %s தட்டச்சிடுகின்றனர்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s தட்டச்சிடுகின்றார்…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "%s பதிவிறக்கப்படுகிறது…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/tr.po0000644000000000000000000011202214452563620013501 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2023-01-30 22:22+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "Dosya, sunucunun azami yükleme boyutunu aşıyor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Mesaj çok uzun" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "düzenlendi" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "bekliyor…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "teslimat başarısız" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Şifrelenmemiş" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Mesaj gönderilemedi" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i dakika önce" msgstr[1] "%i dakıka önce" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Şu an" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Ben" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Resim gönderildi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Dosya gönderildi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Resim alındı" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Dosya alındı" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "Giden arama" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "Gelen arama" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Dün" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Sohbet Başlat" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Sahip" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Yönetici" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Üye" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Kullanıcı" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Davet et" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Özel sohbet başlat" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kov" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Yazma izni ver" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Yazma iznini iptal et" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Görüşmeye Davet et" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Dosya seç" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Seç" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "İptal" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s - %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Bu görüşme mesaj göndermenize izin vermiyor." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "İzin iste" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "Dosya gönder" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Arama başlat" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Sesli arama" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Görüntülü arama" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Mesajları ara" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Üyeler" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "Hata ayıklama bilgileri" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "Aranıyor…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "Çalıyor…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "Bağlanıyor…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s aramayı sonlandırdı" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s aramayı reddetti" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameralar" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kamera bulunamadı." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonlar" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofon bulunamadı." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hoparlörler" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Hoparlör bulunamadı." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Aramaya Davet Et" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Başla" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Kanala katıl" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "İleri" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Katıl" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Geri" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Katılıyor…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Odaya girmek için parola gerekli" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Görüşmeye katılmak veya oluşturmaktan engellendiniz" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Böyle bir oda yok" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Oda oluşturulmasına izin verilmedi" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sadece üyeler odası" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Başka bir takma isim seçin" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Odada çok fazla katılımcı var" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Bağlantı kurulamadı %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Geçersiz adres" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ekle" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "Klavye Kısayolları" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "Dino Hakkında" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Bugün" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i arama sonucu" msgstr[1] "%i arama sonucu" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "%s içinden" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "%s ile" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "Gelen görüntülü arama" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "Gelen görüntülü grup araması" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "Gelen grup araması" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "Reddet" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Kabul et" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Abonelik isteği" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Reddet" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "%s'e davet" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s seni %s'e davet etti" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "İzin isteği" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s, %s için yazma izni istiyor" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "Ayarlar" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "Yerel Ayarlar" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "\"Yazıyor\" bildirimi gönder" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "\"Okundu\" bilgisi gönder" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "Bildirimler" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "Sohbeti sabitle" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "Sohbeti, sohbet listesinin en üstüne sabitler" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "Açık" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "Kapalı" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "Sadece atıf yapıldığında" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "Varsayılan: %s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "İste" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "İzinler" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "Mesaj göndermek için izin isteyin" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "Görüşme Ayrıntıları" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kişi Ayrıntıları" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "Engelle" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "İletişim ve durum güncellemeler her iki taraf için engellenir" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "Oda ismi" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "Oda tanımı" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "Kalıcılık" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "Oda, son katılımcı ayrıldıktan sonra da devam eder" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "Herkese açık aranabilir" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "Katılımcılar başlığı değiştirebilir" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "JID'leri görüntüleme izni" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Katılımcıların JID'lerini kimler görüntüleyebilir?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "Parola" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "Odaya erişimi kısıtlayan bir şifre" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "Moderasyonlu" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "Sadece sesli katılımcılar mesaj gönderebilir" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "Sadece üyeler" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "Sadece üyeler odaya giriş yapabilir" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "Mesaj geçmişi" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "Odada kaydedilecek en fazla mesaj sayısı" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "Oda Ayarları" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Dino'ya hoş geldiniz!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Başlamak için oturum açın veya bir hesap oluşturun." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Hesap oluştur" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Etkin hesap yok" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Hesapları yönet" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Yanlış parola" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Geçersiz TLS Sertifikası" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Hesabı sil %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Sil" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Avatar seç" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Görseller" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Tüm dosyalar" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Bağlandı" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Bağlantı koptu" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Hata" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Hesap Ekle" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "Sunucu, %s olduğunu kanıtlayamadı." #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "Güvenlik sertifikasına işletim sisteminiz güvenmiyor." #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "Güvenlik sertifikası başka bir etki alanına verildi." #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "Güvenlik sertifikası yalnızca gelecekte geçerli olacak." #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "Güvenlik sertifikasının süresi doldu." #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "%s'e giriş yap" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Artık %s hesabını kullanabilirsiniz." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Yalnış kullanıcı adı veya parola" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Bir şeyler yanlış gitti" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Sunucudan yanıt yok" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Kaydol - %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Sunucu bir websitesine kayıt olmanı şart koşuyor" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Web sitesini aç" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Kayıt" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Nasıl kaydolacağınızla ilgili bilgi için %s sayfasına bakın" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "Mesajı düzenle" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Sen" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "Tepki ekle" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Aç" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Farklı kaydet…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ve %i diğerleri yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ve %s yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s ve %s yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s yazıyor…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "Arama başladı" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "%s önce başladı" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "Bu aramayı başka bir aygıtta yaptınız" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "Arama sona erdi" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "%s'de sona erdi" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "%s sürdü" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "Cevapsız arama" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "Bu aramayı kaçırdınız" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s bu aramayı kaçırdı" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "Arama reddedildi" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "Bu aramayı reddettiniz" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s bu aramayı reddetti" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "Arama başarısız" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i saat" msgstr[1] "%i saat" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i dakika" msgstr[1] "%i dakika" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "birkaç saniye" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Bu kişi seni kendi kişiler listesine eklemek istiyor" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "Yanıtla" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "İndiriliyor %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s teklif etti: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Dosya teklif edildi: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Dosya teklif edildi" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Dosya transferi başarısız" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Dosya" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Mesajı güncelle" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Henüz burada sohbet yok" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Sohbet başlatmak veya bir kanala katılmak için +'ya tıklayın" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Hesap" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Takma isim" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Mahlas" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kişi Ekle" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "Mesaj ulaştığında bildirim gönder" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "İfadeleri emoji olarak göster" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "Yazım denetimi" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "Modern XMPP Sohbet İstemcisi" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet " "uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve " "güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır." #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve \"okundu\" bilgisi, " "\"yazıyor...\" bildirimi gibi gizlilikle alakalı özelliklerin " "ayarlanabilmesini sağlar." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara " "senkronize eder." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Henüz arama yapılmadı" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Arama başlatmak için bir şey yaz" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Hiç mesaj bulunamadı" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Yazım hatalarını kontrol et ya da filtreleri kaldırmayı dene" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Gönder" #: main/data/shortcuts.ui:10 msgid "General" msgstr "Genel" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "Sohbet" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "Navigasyon" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "Bir sonraki sohbete geç" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "Bir önceki sohbete geç" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "Hesaplar" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "Yerel mahlas" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "Hiç hesap ayarlanmamış" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "Bir hesap ekle" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "Giriş yap" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "Hesap oluştur" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "Güvenli bir bağlantı oluşturulamadı" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "Bağlan" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "Genel bir sunucu seç" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "Veya özel bir sunucu adresi" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "Bunun yerine oturum açın" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "Başka sunucu seç" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "Hepsi tamam!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "Bitir" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Bir sohbet başlatmak ya da kanala katılmak için buraya tıkla." #~ msgid "Video call incoming" #~ msgstr "Gelen görüntülü arama" #~ msgid "Call incoming" #~ msgstr "Gelen arama" #~ msgid "Establishing call" #~ msgstr "Arama oluşturuluyor" #~ msgid "Video call establishing" #~ msgstr "Görüntülü arama oluşturuluyor" #~ msgid "Call establishing" #~ msgstr "Arama oluşturuluyor" #~ msgid "Call in progress…" #~ msgstr "Arama devam ediyor…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "%s sürdü" #~ msgid "seconds" #~ msgstr "saniye" #~ msgid "No active conversations" #~ msgstr "Aktif sohbet yok" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s ve %i diğerleri" #~ msgid "You can now start using %s" #~ msgstr "Şimdi %s kullanmaya başlayabilirsin" #~ msgid "Open Registration" #~ msgstr "Açık Kaydolma" #~ msgid "Save" #~ msgstr "Kaydet" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ve %s" #~ msgid "%s and %s" #~ msgstr "%s ve %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "yazıyor…" #~ msgstr[1] "yazıyorlar…" #~ msgid "has stopped typing" #~ msgstr "yazmayı sonlandırdı" #~ msgid "%i search results" #~ msgstr "%i adet sonuc bulundu" #~ msgid "Discover real JIDs" #~ msgstr "Gerçek JIDs keşfetme" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kimler gerçek JIDs keşfedebilir?" #~ msgid "Password required for room entry, if any" #~ msgstr "Odaya giriş için yapmak için parola ( gerekirse )" dino-0.4.3/main/po/uk.po0000644000000000000000000010453314452563620013503 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-08-26 22:15+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.14-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "Повідомлення задовге" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "відредаговано" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Нешифровано" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "Не вдалося надіслати повідомлення" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%H∶%M, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%l∶%M, %x %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i хвилина тому" msgstr[1] "%i хвилини тому" msgstr[2] "%i хвилин тому" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "Щойно" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "Я" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Зображення надіслано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Файл надіслано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Зображення отримано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Файл отримано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Учора" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "Почати розмову" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Власник" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Адміністратор" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Учасник" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Користувач" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Запросити" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Почати приватну розмову" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Вигнати" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Видати дозвіл писати" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Відкликати дозвіл писати" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Запросити до конференції" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "Виберіть файл" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "Вибрати" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Скасувати" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s з %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Вам заборонено відправляти повідомлення в цій бесіді." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Попросити дозвіл" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "Пошук повідомлень" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Члени" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "З'єднання…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "Почати" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "Приєднатись до каналу" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "Далі" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "Приєднатися" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "Назад" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Приєднання…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Для входу в кімнату потрібно ввести пароль" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Вам заборонено приєднуватися або створювати конференції" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Кімната не існує" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Вам заборонено створювати кімнати" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Кімната тільки для учасників" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Оберіть інший нікнейм" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "У кімнаті занадто багато відвідувачів" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "Не вдалося підключитися до %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "Недійсна адреса" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Додати" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Сьогодні" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i результат пошуку" msgstr[1] "%i результати пошуку" msgstr[2] "%i результатів пошуку" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Прийняти" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "Необхідна авторизація" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "Відхилити" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "Запрошення до %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s запросив вас до %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "Запит на дозвіл" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s запитує дозвіл писати в %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "Ласкаво просимо в Діно!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "Увійдіть в систему або створіть обліковий запис, щоб почати." #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "Налаштування облікового запису" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "Немає активних облікових записів" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "Керування обліковими записами" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "Хибний пароль" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "Недійсний сертифікат TLS" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "Видалити обліковий запис %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "Видалити" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "Вибрати аватар" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "Зображення" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "Усі файли" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "Під’єднано" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "Роз'єднано" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "Помилка" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "Додати обліковий запис" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "Вхід в обліковий запис %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "Тепер ви можете використовувати обліковий запис %s." #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "Неправильне ім'я користувача чи пароль" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "Щось пішло не так" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "Немає відповіді від сервера" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "Зареєструватись в %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "Сервер вимагає реєстрації через веб-сайт" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "Відкрити веб-сайт" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "Зареєструватися" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "Прочитайте %s, щоб дізнатися про процес реєстрації" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s та %i інших друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s та %s друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s і %s друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s друкує…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "Цей контакт хоче додати вас до свого списку контактів" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "Завантаження %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "%s приблизний розмір: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "Приблизний розмір файлу: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "Запропонований Файл" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "Помилка передачі файлу" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/shortcuts.ui:10 msgid "General" msgstr "" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "" dino-0.4.3/main/po/zh_CN.po0000644000000000000000000011107114452563620014060 0ustar rootroot# Chinese translations for PACKAGE package # PACKAGE 软件包的简体中文翻译. # Copyright (C) 2017 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Tong Hui , 2017. msgid "" msgstr "" "Project-Id-Version: dino\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-12 11:00+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "文件超过了服务器最大上传大小限制。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "消息太长" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "已編辑" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "发送中…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "发送失败" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "未加密" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "无法发送消息" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x,%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%_m 月 %_d 日,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%_m 月 %_d 日,%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%A,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%A,%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分钟以前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "刚刚" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "我" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "图片已发送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "文件已发送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "图片已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "文件已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "呼出通话" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "呼入通话" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%_m 月 %_d 日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨天" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "开始聊天" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "所有者" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理员" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "成员" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "用户" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "邀请" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "启动私密会话" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "踢出" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "授予写入权限" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "撤回写入权限" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "邀请到聊天室里" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "选择文件" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "选择" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "取消" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s 来自 %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "该会议不允许你发送讯息。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "请求权限" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "发送一个文件" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "开始通话" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "音频通话" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "视频通话" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "搜索消息" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "成员" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "调试信息" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "正在呼叫…" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "对方已响铃…" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "连接中…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "%s 结束了通话" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "%s 拒绝了通话" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "摄像头" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "未找到摄像头。" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "麦克风" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "未找到麦克风。" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "扬声器" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "未找到扬声器。" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "邀请加入通话" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "开始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "加入频道" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "下一个" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "加入" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "返回" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "加入中…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "需要输入密码才能加入聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "被禁止加入或创建聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "聊天室不存在" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "不允许创建聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "聊天室不对外开放" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "选择一个不同的昵称" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "聊天室内人数过多" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "无法连接到 %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "地址无效" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "添加" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "键盘快捷键" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "关于 Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今天" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%a,%b%d日" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 条搜索结果" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "在%s中" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "用%s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "传入视频通话" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "传入多人视频通话" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "传入多人通话" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "拒绝" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "接受" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "订阅请求" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "拒绝" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "对 %s 的邀请" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s邀请你加入 %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "权限请求" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s 请求写入 %s 的权限" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "设置" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "本地设置" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "发送打字通知" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "发送已读回执" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "通知" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "开启" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "关闭" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "只有被提到时" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "默认:%s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "请求" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "权限" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "请求发送讯息的权限" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "聊天室详情" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "联系人详情" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "封禁" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "双向通讯和状态更新已经被封禁" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "聊天室名称" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "聊天室描述" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "一直在线" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "聊天室在最后一个成员离开后仍然保留" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "可被公开检索到" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "参与者可以更改主题" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "查看JID的权限" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "谁被允许查看成员的JID?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "密码" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "一个限制对该聊天室访问的密码" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "主持" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "仅限有发言权的参与者可以发送消息" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "仅限成员" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "只有成员才可以进入聊天室" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "消息历史记录" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "聊天室储存的历史消息的最大数目" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "聊天室设置" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "欢迎来到 Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "登录或者创建一个账户后开始。" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "设置帐户" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "没有活动的帐号" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "管理帐号" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "密码错误" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "无效的 TLS 证书" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "移除账户 %s?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "删除" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "选择头像" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "图片" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "所有文件" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "已连接" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "已断开连接" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "错误" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "添加帐号" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "服务器无法证明它是 %s。" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "其安全证书不受你的操作系统信任。" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "其安全证书已颁发给另一个域名。" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "其安全证书只会在将来有效。" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "其安全证书已过期。" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "登录到%s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "你现在可以使用账户 %s 了。" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "错误的用户名或密码" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "出错了" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "服务器无响应" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "在 %s 注册" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "服务器要求在网站上注册" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "打开网站" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "注册" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "检查 %s 以获取如何注册的信息" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "打开" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "另存为…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s 和其他 %i 个人正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s 和 %s 正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s 和 %s 正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s 正在输入…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "通话已开始" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "开始于%s前" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "您在另一个设备上处理了该通话" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "通话已结束" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "结束于%s" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "持续了%s" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "通话已错过" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "您错过了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "%s错过了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "通话已拒绝" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "您拒绝了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "%s拒绝了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "通话失败" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i小时" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i分钟" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "几秒" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "该联系人想把你加到他们的联系人列表" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "正在下载 %s …" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "分享了 %s:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "分享了文件:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "分享的文件" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "文件传输失败" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "文件" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "更新讯息" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "你没有开启的对话" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "账号" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "昵称" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "别名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "增加联系人" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "新消息到达时通知" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "将笑脸转换成 Emoji" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "检查拼写" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "现代 XMPP 聊天客户端" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino 是一个现代的开源聊天桌面客户端。它致力于提供一个清爽又可靠的 Jabber/" "XMPP 体验,同时又保护您的隐私。" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "它支持 OMEMO 和 OpenPGP 端对端加密并允许配置隐私相关的特性比如已读回执和输入" "提醒。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino 从服务器获取消息并和其他设备同步。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "无活动的搜索" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "输入以开始搜索" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "无匹配的消息" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "检查拼写或尝试移除过滤器" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "发送" #: main/data/shortcuts.ui:10 msgid "General" msgstr "常规" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "对话" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "导航" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "转到下一个对话" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "转到上一个对话" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "帐号" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "本地别名" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "没有配置账户" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "添加新账号" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "登录" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "创建账户" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "无法建立安全连接" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "连接" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "选择一个公共服务器" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "或指定一个服务器地址" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "代替登录" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "选择另外一个服务器" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "都准备好了!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "完成" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "点击此处以开始对话或加入频道。" #~ msgid "No active conversations" #~ msgstr "没有活动的会话" #~ msgid "Main window with conversations" #~ msgstr "带有对话的主窗口" #~ msgid "%s, %s and %i others" #~ msgstr "%s、%s 以及 %i 个其他人" #~ msgid "You can now start using %s" #~ msgstr "您现在可以开始使用%s" #~ msgid "Open Registration" #~ msgstr "开放注册" #~ msgid "Save" #~ msgstr "保存" #~ msgid "%s, %s and %s" #~ msgstr "%s、%s 和 %s" #~ msgid "%s and %s" #~ msgstr "%s 和 %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "正在输入…" #~ msgid "has stopped typing" #~ msgstr "已经停止输入" #~ msgid "%i search results" #~ msgstr "%i 搜索结果" #~ msgid "Discover real JIDs" #~ msgstr "发现真实 JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "谁可能发现真实 JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "如果有的话需要密码才能进入房间" #~ msgid "Failed connecting to %s" #~ msgstr "连接 %s 失败" #~ msgid "Join Conference" #~ msgstr "加入群聊" #~ msgid "Communicate happiness." #~ msgstr "沟通快乐。" #~ msgid "Preferences" #~ msgstr "选项" #~ msgid "Quit" #~ msgstr "退出" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID 必须形如 “user@example.com”" #~ msgid "Copy Link Address" #~ msgstr "复制链接地址" #~ msgid "Copy" #~ msgstr "复制" #~ msgid "Select All" #~ msgstr "全选" #~ msgid "Search" #~ msgstr "搜索" #~ msgid "Send message marker" #~ msgstr "发送消息标记" #~ msgid "Start Chat" #~ msgstr "开始聊天" #~ msgid "Request presence updates" #~ msgstr "请求在线更新" #~ msgid "Join on startup" #~ msgstr "启动时加入" #~ msgid "Add Chat" #~ msgstr "添加聊天" dino-0.4.3/main/po/zh_TW.po0000644000000000000000000010612614452563620014117 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-12-10 19:29+0000\n" "Language-Team: none\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: main/src/ui/file_send_overlay.vala:92 msgid "The file exceeds the server's maximum upload size." msgstr "檔案大小超過了伺服器的的上傳限制。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:81 msgid "Message too long" msgstr "訊息太長" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:107 msgid "edited" msgstr "已編輯" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:116 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:129 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:163 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "未加密" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:201 msgid "Unable to send message" msgstr "無法傳送訊息" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:238 #, no-c-format msgid "%x, %H∶%M" msgstr "%x,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:239 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x,%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b%d日,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:243 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b%d日,%p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%a, %H∶%M" msgstr "%A,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:247 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%A,%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:358 #: main/src/ui/conversation_content_view/call_widget.vala:178 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:251 #, no-c-format msgid "%l∶%M %p" msgstr "%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:361 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:255 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分鐘前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:363 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:257 msgid "Just now" msgstr "剛才" #: main/src/ui/conversation_selector/conversation_selector_row.vala:172 #: main/src/ui/conversation_selector/conversation_selector_row.vala:197 #: main/src/ui/conversation_selector/conversation_selector_row.vala:212 #: main/src/ui/util/helper.vala:122 main/src/ui/util/helper.vala:126 #: main/src/ui/util/helper.vala:134 msgid "Me" msgstr "我" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "影像已傳送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:203 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "檔案已傳送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "影像已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "檔案已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:214 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 #: main/src/ui/conversation_content_view/call_widget.vala:171 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:350 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b%d日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:354 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨天" #: main/src/ui/conversation_list_titlebar.vala:27 #: main/src/ui/add_conversation/select_contact_dialog.vala:99 #: main/data/menu_add.ui:7 main/data/shortcuts.ui:14 msgid "Start Conversation" msgstr "開始對話" #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "主人" #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理員" #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "會員" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "使用者" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "邀請" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "開始私人對話" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "踢走" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "同意寫入權限" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "撤回寫入權限" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "邀請加入聊天室" #: main/src/ui/conversation_view_controller.vala:214 msgid "Select file" msgstr "選取檔案" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select" msgstr "選擇" #: main/src/ui/conversation_view_controller.vala:214 #: main/src/ui/add_conversation/select_contact_dialog.vala:39 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/add_conversation/add_conference_dialog.vala:118 #: main/src/ui/application.vala:298 main/src/ui/manage_accounts/dialog.vala:146 #: main/src/ui/conversation_content_view/file_default_widget.vala:70 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "取消" #: main/src/ui/util/helper.vala:118 #, c-format msgid "%s from %s" msgstr "%s 來自 %s" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "此聊天室不允許您傳送訊息。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "請求權限" #: main/src/ui/chat_input/view.vala:44 main/data/file_send_overlay.ui:24 #: main/data/shortcuts.ui:37 msgid "Send a file" msgstr "傳送檔案" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/shortcuts.ui:31 msgid "Search messages" msgstr "搜尋訊息" #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "會員" #: main/src/ui/call_window/participant_widget.vala:118 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:127 #: main/src/ui/conversation_content_view/call_widget.vala:148 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:129 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:131 #: main/src/ui/manage_accounts/dialog.vala:216 msgid "Connecting…" msgstr "正在連線…" #: main/src/ui/call_window/call_window.vala:191 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:193 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:100 msgid "Start" msgstr "開始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:298 main/data/menu_add.ui:11 #: main/data/shortcuts.ui:20 msgid "Join Channel" msgstr "加入聊天室" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/src/ui/add_conversation/add_conference_dialog.vala:114 #: main/data/manage_accounts/add_account_dialog.ui:94 #: main/data/manage_accounts/add_account_dialog.ui:353 #: main/data/manage_accounts/add_account_dialog.ui:426 msgid "Next" msgstr "下一個" #: main/src/ui/add_conversation/add_conference_dialog.vala:63 #: main/src/ui/add_conversation/add_conference_dialog.vala:138 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:298 msgid "Join" msgstr "加入" #: main/src/ui/add_conversation/add_conference_dialog.vala:143 #: main/data/manage_accounts/add_account_dialog.ui:160 #: main/data/manage_accounts/add_account_dialog.ui:226 msgid "Back" msgstr "返回" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "正在加入…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "需要密碼才能加入聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "被禁止加入或建立聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "聊天室不存在" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "不允許建立聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "聊天室不對外開放" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "選取一個不同的暱稱" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "聊天室人數過多" #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:185 #: main/src/ui/manage_accounts/add_account_dialog.vala:261 #, c-format msgid "Could not connect to %s" msgstr "無法連線到 %s" #: main/src/ui/add_conversation/conference_details_fragment.vala:186 #: main/src/ui/manage_accounts/add_account_dialog.vala:266 #: main/src/ui/manage_accounts/add_account_dialog.vala:299 #: main/src/ui/manage_accounts/add_account_dialog.vala:309 #: main/src/ui/manage_accounts/add_account_dialog.vala:402 msgid "Invalid address" msgstr "無效的位址" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "新增" #: main/src/ui/application.vala:204 main/data/menu_app.ui:17 msgid "Keyboard Shortcuts" msgstr "鍵盤快捷鍵" #: main/src/ui/application.vala:284 main/data/menu_app.ui:21 msgid "About Dino" msgstr "關於 Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今天" #: main/src/ui/widgets/date_separator.vala:35 msgid "%a, %b %d" msgstr "%b%d日,%A" #: main/src/ui/global_search.vala:179 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 個搜尋結果" #: main/src/ui/global_search.vala:206 #, c-format msgid "In %s" msgstr "在 %s" #: main/src/ui/global_search.vala:206 #, c-format msgid "With %s" msgstr "和 %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:113 #: main/src/ui/conversation_content_view/call_widget.vala:133 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:115 #: main/src/ui/conversation_content_view/call_widget.vala:135 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:131 #: main/src/ui/notifier_gnotifications.vala:147 #: main/src/ui/notifier_freedesktop.vala:123 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:220 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:37 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "同意" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:155 msgid "Subscription request" msgstr "線上狀態訂閱請求" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:130 #: main/src/ui/notifier_gnotifications.vala:146 #: main/src/ui/notifier_freedesktop.vala:161 #: main/src/ui/notifier_freedesktop.vala:253 #: main/src/ui/conversation_content_view/subscription_notification.vala:38 msgid "Deny" msgstr "拒絕" #: main/src/ui/notifier_gnotifications.vala:120 #: main/src/ui/notifier_freedesktop.vala:211 #, c-format msgid "Invitation to %s" msgstr "邀請加入 %s" #: main/src/ui/notifier_gnotifications.vala:121 #: main/src/ui/notifier_freedesktop.vala:212 #, c-format msgid "%s invited you to %s" msgstr "%s 邀請您加入 %s" #: main/src/ui/notifier_gnotifications.vala:138 #: main/src/ui/notifier_freedesktop.vala:244 msgid "Permission request" msgstr "權限請求" #: main/src/ui/notifier_gnotifications.vala:139 #: main/src/ui/notifier_freedesktop.vala:245 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s 請求權限以寫入 %s" #: main/src/ui/contact_details/settings_provider.vala:12 #: main/src/ui/contact_details/blocking_provider.vala:31 #: main/data/settings_dialog.ui:4 main/data/menu_app.ui:11 msgid "Settings" msgstr "設定" #: main/src/ui/contact_details/settings_provider.vala:13 msgid "Local Settings" msgstr "本地設定" #: main/src/ui/contact_details/settings_provider.vala:28 #: main/data/settings_dialog.ui:23 msgid "Send typing notifications" msgstr "傳送「正在輸入」提示" #: main/src/ui/contact_details/settings_provider.vala:33 #: main/data/settings_dialog.ui:32 msgid "Send read receipts" msgstr "傳送已讀回條" #: main/src/ui/contact_details/settings_provider.vala:38 #: main/src/ui/contact_details/settings_provider.vala:47 msgid "Notifications" msgstr "通知" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pin conversation" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:55 msgid "Pins the conversation to the top of the conversation list" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:89 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:131 msgid "On" msgstr "啓用" #: main/src/ui/contact_details/settings_provider.vala:91 #: main/src/ui/contact_details/settings_provider.vala:129 #: main/src/ui/contact_details/settings_provider.vala:132 msgid "Off" msgstr "停用" #: main/src/ui/contact_details/settings_provider.vala:93 msgid "Only when mentioned" msgstr "僅限被提及時" #: main/src/ui/contact_details/settings_provider.vala:95 #: main/src/ui/contact_details/settings_provider.vala:130 #, c-format msgid "Default: %s" msgstr "預設:%s" #: main/src/ui/contact_details/permissions_provider.vala:23 msgid "Request" msgstr "請求" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Permissions" msgstr "權限" #: main/src/ui/contact_details/permissions_provider.vala:25 msgid "Request permission to send messages" msgstr "請求權限以傳送訊息" #: main/src/ui/contact_details/dialog.vala:37 msgid "Conference Details" msgstr "聊天室詳細資訊" #: main/src/ui/contact_details/dialog.vala:37 main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "聯絡人詳細資訊" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Block" msgstr "封鎖" #: main/src/ui/contact_details/blocking_provider.vala:31 msgid "Communication and status updates in either direction are blocked" msgstr "封鎖雙方向的通訊以及狀態更新" #: main/src/ui/contact_details/muc_config_form_provider.vala:50 msgid "Name of the room" msgstr "聊天室名稱" #: main/src/ui/contact_details/muc_config_form_provider.vala:53 msgid "Description of the room" msgstr "聊天室介紹" #: main/src/ui/contact_details/muc_config_form_provider.vala:56 msgid "Persistent" msgstr "保留" #: main/src/ui/contact_details/muc_config_form_provider.vala:57 msgid "The room will persist after the last occupant leaves" msgstr "聊天室在最後一名成員離開後仍然保留" #: main/src/ui/contact_details/muc_config_form_provider.vala:60 msgid "Publicly searchable" msgstr "可被公開搜尋" #: main/src/ui/contact_details/muc_config_form_provider.vala:63 msgid "Occupants may change the subject" msgstr "成員可以變更主題" #: main/src/ui/contact_details/muc_config_form_provider.vala:66 msgid "Permission to view JIDs" msgstr "JID 檢視權限" #: main/src/ui/contact_details/muc_config_form_provider.vala:67 msgid "Who is allowed to view the occupants' JIDs?" msgstr "誰可以檢視成員的 JID?" #: main/src/ui/contact_details/muc_config_form_provider.vala:70 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 #: main/data/manage_accounts/dialog.ui:151 #: main/data/manage_accounts/add_account_dialog.ui:194 msgid "Password" msgstr "密碼" #: main/src/ui/contact_details/muc_config_form_provider.vala:71 msgid "A password to restrict access to the room" msgstr "加入聊天室需要的密碼" #: main/src/ui/contact_details/muc_config_form_provider.vala:74 msgid "Moderated" msgstr "發言" #: main/src/ui/contact_details/muc_config_form_provider.vala:75 msgid "Only occupants with voice may send messages" msgstr "僅限有發言權的成員可以傳送訊息" #: main/src/ui/contact_details/muc_config_form_provider.vala:78 msgid "Members only" msgstr "僅限會員" #: main/src/ui/contact_details/muc_config_form_provider.vala:79 msgid "Only members may enter the room" msgstr "只有會員才可以加入聊天室" #: main/src/ui/contact_details/muc_config_form_provider.vala:82 msgid "Message history" msgstr "聊天紀錄" #: main/src/ui/contact_details/muc_config_form_provider.vala:83 msgid "Maximum amount of backlog issued by the room" msgstr "聊天室儲存的最大聊天紀錄數目" #: main/src/ui/contact_details/muc_config_form_provider.vala:89 msgid "Room Configuration" msgstr "聊天室設定" #: main/src/ui/main_window.vala:179 msgid "Welcome to Dino!" msgstr "歡迎來到 Dino!" #: main/src/ui/main_window.vala:180 msgid "Sign in or create an account to get started." msgstr "開始前請先登入或建立一個帳號。" #: main/src/ui/main_window.vala:181 msgid "Set up account" msgstr "設定帳號" #: main/src/ui/main_window.vala:188 msgid "No active accounts" msgstr "没有啓用的帳號" #: main/src/ui/main_window.vala:189 msgid "Manage accounts" msgstr "管理帳號" #: main/src/ui/notifier_freedesktop.vala:189 #: main/src/ui/manage_accounts/dialog.vala:229 msgid "Wrong password" msgstr "密碼錯誤" #: main/src/ui/notifier_freedesktop.vala:192 #: main/src/ui/manage_accounts/dialog.vala:231 msgid "Invalid TLS certificate" msgstr "無效的 TLS 憑證" #: main/src/ui/manage_accounts/dialog.vala:116 #, c-format msgid "Remove account %s?" msgstr "移除帳號 %s ?" #: main/src/ui/manage_accounts/dialog.vala:119 msgid "Remove" msgstr "移除" #: main/src/ui/manage_accounts/dialog.vala:146 msgid "Select avatar" msgstr "選取頭像" #: main/src/ui/manage_accounts/dialog.vala:153 msgid "Images" msgstr "影像" #: main/src/ui/manage_accounts/dialog.vala:157 msgid "All files" msgstr "所有檔案" #: main/src/ui/manage_accounts/dialog.vala:218 msgid "Connected" msgstr "已連線" #: main/src/ui/manage_accounts/dialog.vala:220 msgid "Disconnected" msgstr "連線已斷開" #: main/src/ui/manage_accounts/dialog.vala:234 #: main/src/ui/manage_accounts/dialog.vala:236 msgid "Error" msgstr "錯誤" #: main/src/ui/manage_accounts/add_account_dialog.vala:82 msgid "Add Account" msgstr "新增帳號" #: main/src/ui/manage_accounts/add_account_dialog.vala:153 #, c-format msgid "The server could not prove that it is %s." msgstr "該伺服器無法證明其為 %s。" #: main/src/ui/manage_accounts/add_account_dialog.vala:155 msgid "Its security certificate is not trusted by your operating system." msgstr "其安全憑證不被您的作業系統信任。" #: main/src/ui/manage_accounts/add_account_dialog.vala:157 msgid "Its security certificate is issued to another domain." msgstr "其安全憑證已被簽發給另一域名。" #: main/src/ui/manage_accounts/add_account_dialog.vala:159 msgid "Its security certificate will only become valid in the future." msgstr "其安全憑證在未來方會生效。" #: main/src/ui/manage_accounts/add_account_dialog.vala:161 msgid "Its security certificate is expired." msgstr "其安全憑證已過時。" #: main/src/ui/manage_accounts/add_account_dialog.vala:180 #, c-format msgid "Sign in to %s" msgstr "登入到 %s" #: main/src/ui/manage_accounts/add_account_dialog.vala:224 #, c-format msgid "You can now use the account %s." msgstr "您現在可以使用帳號 %s 了。" #: main/src/ui/manage_accounts/add_account_dialog.vala:281 msgid "Wrong username or password" msgstr "使用者名稱或密碼錯誤" #: main/src/ui/manage_accounts/add_account_dialog.vala:284 msgid "Something went wrong" msgstr "出現了錯誤" #: main/src/ui/manage_accounts/add_account_dialog.vala:327 msgid "No response from server" msgstr "伺服器没有回應" #: main/src/ui/manage_accounts/add_account_dialog.vala:338 #, c-format msgid "Register on %s" msgstr "在 %s 註冊" #: main/src/ui/manage_accounts/add_account_dialog.vala:341 msgid "The server requires to sign up through a website" msgstr "伺服器要求通過網頁進行註冊" #: main/src/ui/manage_accounts/add_account_dialog.vala:343 msgid "Open website" msgstr "開啓網頁" #: main/src/ui/manage_accounts/add_account_dialog.vala:364 msgid "Register" msgstr "註冊" #: main/src/ui/manage_accounts/add_account_dialog.vala:366 #, c-format msgid "Check %s for information on how to sign up" msgstr "查閱 %s 以取得關於如何註冊的資訊" #: main/src/ui/conversation_content_view/message_widget.vala:213 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:8 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:54 #: main/src/ui/conversation_content_view/file_widget.vala:171 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:110 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s 和其他 %i 個人正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:112 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s 和 %s 正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:114 #, c-format msgid "%s and %s are typing…" msgstr "%s 和 %s 正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:116 #, c-format msgid "%s is typing…" msgstr "%s 正在輸入…" #: main/src/ui/conversation_content_view/call_widget.vala:155 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:157 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:172 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:180 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:182 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:186 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:191 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:196 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:201 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:206 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:230 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" #: main/src/ui/conversation_content_view/call_widget.vala:237 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" #: main/src/ui/conversation_content_view/call_widget.vala:244 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:48 msgid "This contact would like to add you to their contact list" msgstr "此聯絡人想把您新增到他們的聯絡人清單" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:33 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:43 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:64 #, c-format msgid "Downloading %s…" msgstr "正在下載 %s …" #: main/src/ui/conversation_content_view/file_default_widget.vala:77 #, c-format msgid "%s offered: %s" msgstr "分享了 %s:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:79 #, c-format msgid "File offered: %s" msgstr "分享了檔案:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:81 msgid "File offered" msgstr "分享的檔案" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 msgid "File transfer failed" msgstr "檔案傳送失敗" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "更新訊息" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "您没有開啓的對話" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "帳號" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "暱稱" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "別名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "新增聯絡人" #: main/data/settings_dialog.ui:41 msgid "Notify when a new message arrives" msgstr "有新訊息時提醒" #: main/data/settings_dialog.ui:50 msgid "Convert smileys to emojis" msgstr "將文字表情轉換爲表情圖標" #: main/data/settings_dialog.ui:59 msgid "Check spelling" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:7 msgid "Modern XMPP Chat Client" msgstr "現代化的 XMPP 用戶端聊天軟件" #: main/data/im.dino.Dino.appdata.xml.in:9 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而" "可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已" "讀回條和正在輸入提示。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino 從伺服器取得訊息並與其他裝置同步。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "目前没有搜尋" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "輸入關鍵字進行搜尋" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "找不到符合條件的訊息" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "檢查拼字或嘗試移除篩選器" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "傳送" #: main/data/shortcuts.ui:10 msgid "General" msgstr "一般" #: main/data/shortcuts.ui:27 msgid "Conversation" msgstr "對話" #: main/data/shortcuts.ui:44 msgid "Navigation" msgstr "導航" #: main/data/shortcuts.ui:48 msgid "Jump to next conversation" msgstr "跳到下一個對話" #: main/data/shortcuts.ui:54 msgid "Jump to previous conversation" msgstr "跳到上一個對話" #: main/data/menu_app.ui:7 main/data/manage_accounts/dialog.ui:5 msgid "Accounts" msgstr "帳號" #: main/data/manage_accounts/dialog.ui:176 msgid "Local alias" msgstr "本地別名" #: main/data/manage_accounts/dialog.ui:225 msgid "No accounts configured" msgstr "未有設定好的帳號" #: main/data/manage_accounts/dialog.ui:233 msgid "Add an account" msgstr "新增帳號" #: main/data/manage_accounts/add_account_dialog.ui:35 msgid "Sign in" msgstr "登入" #: main/data/manage_accounts/add_account_dialog.ui:75 #: main/data/manage_accounts/add_account_dialog.ui:284 msgid "Create account" msgstr "建立帳號" #: main/data/manage_accounts/add_account_dialog.ui:142 msgid "Could not establish a secure connection" msgstr "無法建立安全連線" #: main/data/manage_accounts/add_account_dialog.ui:245 msgid "Connect" msgstr "連線" #: main/data/manage_accounts/add_account_dialog.ui:294 msgid "Choose a public server" msgstr "選取一個公眾伺服器" #: main/data/manage_accounts/add_account_dialog.ui:318 msgid "Or specify a server address" msgstr "或指定一個伺服器位址" #: main/data/manage_accounts/add_account_dialog.ui:334 msgid "Sign in instead" msgstr "登入" #: main/data/manage_accounts/add_account_dialog.ui:408 msgid "Pick another server" msgstr "選取另一臺伺服器" #: main/data/manage_accounts/add_account_dialog.ui:476 msgid "All set up!" msgstr "全部設定好了!" #: main/data/manage_accounts/add_account_dialog.ui:506 msgid "Finish" msgstr "完成" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "點擊此處開始對話或加入聊天室。" #~ msgid "No active conversations" #~ msgstr "没有進行中的對話" #~ msgid "Main window with conversations" #~ msgstr "含有對話的主視窗" #~ msgid "%s, %s and %i others" #~ msgstr "%s、%s 和另外 %i 人" #~ msgid "You can now start using %s" #~ msgstr "您現在可以開始使用 %s" #~ msgid "Open Registration" #~ msgstr "開啟網頁" #~ msgid "Save" #~ msgstr "儲存" #~ msgid "%s, %s and %s" #~ msgstr "%s、%s 和 %s" #~ msgid "%s and %s" #~ msgstr "%s 和 %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "正在輸入…" #~ msgid "has stopped typing" #~ msgstr "已經停止輸入" #~ msgid "%i search results" #~ msgstr "%i 個搜尋結果" #~ msgid "Discover real JIDs" #~ msgstr "檢閱真正的 JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "誰可以檢閱真正的 JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "加入聊天室需要的密碼,可不填" dino-0.4.3/main/src/0000755000000000000000000000000014452563620012667 5ustar rootrootdino-0.4.3/main/src/main.vala0000644000000000000000000000147414452563620014466 0ustar rootrootusing Dino.Entities; using Dino.Ui; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino { void main(string[] args) { try{ string? exec_path = args.length > 0 ? args[0] : null; SearchPathGenerator search_path_generator = new SearchPathGenerator(exec_path); Intl.textdomain(GETTEXT_PACKAGE); internationalize(GETTEXT_PACKAGE, search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR)); Gtk.init(); Dino.Ui.Application app = new Dino.Ui.Application() { search_path_generator=search_path_generator }; Plugins.Loader loader = new Plugins.Loader(app); loader.load_all(); app.run(args); loader.shutdown(); } catch (Error e) { warning(@"Fatal error: $(e.message)"); } } } dino-0.4.3/main/src/ui/0000755000000000000000000000000014452563620013304 5ustar rootrootdino-0.4.3/main/src/ui/add_conversation/0000755000000000000000000000000014452563620016626 5ustar rootrootdino-0.4.3/main/src/ui/add_conversation/add_conference_dialog.vala0000644000000000000000000001755314452563620023744 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Xep; namespace Dino.Ui { public class AddConferenceDialog : Gtk.Dialog { private Stack stack = new Stack(); private Button cancel_button = new Button(); private Button ok_button; private SelectJidFragment select_fragment; private ConferenceDetailsFragment details_fragment; private ConferenceList conference_list; private ListBox conference_list_box; private StreamInteractor stream_interactor; public AddConferenceDialog(StreamInteractor stream_interactor) { Object(use_header_bar : Util.use_csd() ? 1 : 0); this.title = _("Join Channel"); this.modal = true; this.stream_interactor = stream_interactor; stack.visible = true; stack.vhomogeneous = false; get_content_area().append(stack); setup_headerbar(); setup_jid_add_view(); setup_conference_details_view(); show_jid_add_view(); } private void show_jid_add_view() { // Rewire headerbar (if CSD) if (Util.use_csd()) { cancel_button.set_label(_("Cancel")); cancel_button.clicked.disconnect(show_jid_add_view); cancel_button.clicked.connect(on_cancel); ok_button.label = _("Next"); ok_button.sensitive = select_fragment.done; ok_button.clicked.connect(on_next_button_clicked); details_fragment.fragment_active = false; details_fragment.notify["done"].disconnect(set_ok_sensitive_from_details); select_fragment.notify["done"].connect(set_ok_sensitive_from_select); } stack.transition_type = StackTransitionType.SLIDE_RIGHT; stack.set_visible_child_name("select"); } private void show_conference_details_view() { // Rewire headerbar (if CSD) if (Util.use_csd()) { cancel_button.set_icon_name("go-previous-symbolic"); cancel_button.clicked.disconnect(on_cancel); cancel_button.clicked.connect(show_jid_add_view); ok_button.label = _("Join"); ok_button.sensitive = details_fragment.done; ok_button.clicked.disconnect(on_next_button_clicked); details_fragment.fragment_active = true; select_fragment.notify["done"].disconnect(set_ok_sensitive_from_select); details_fragment.notify["done"].connect(set_ok_sensitive_from_details); } stack.transition_type = StackTransitionType.SLIDE_LEFT; stack.set_visible_child_name("details"); animate_window_resize(details_fragment); } private void setup_headerbar() { ok_button = new Button() { can_focus=true }; ok_button.add_css_class("suggested-action"); if (Util.use_csd()) { HeaderBar header_bar = get_header_bar() as HeaderBar; header_bar.show_title_buttons = false; header_bar.pack_start(cancel_button); header_bar.pack_end(ok_button); // ok_button.has_default = true; } } private void setup_jid_add_view() { conference_list = new ConferenceList(stream_interactor); conference_list_box = conference_list.get_list_box(); conference_list_box.row_activated.connect(() => { ok_button.clicked(); }); select_fragment = new SelectJidFragment(stream_interactor, conference_list_box, stream_interactor.get_accounts()); select_fragment.add_jid.connect((row) => { AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); dialog.set_transient_for(this); dialog.present(); }); select_fragment.remove_jid.connect((row) => { ConferenceListRow conference_row = row as ConferenceListRow; stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark); }); Box wrap_box = new Box(Orientation.VERTICAL, 0); wrap_box.append(select_fragment); stack.add_named(wrap_box, "select"); if (!Util.use_csd()) { Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 }; Button ok_button = new Button.with_label(_("Next")) { sensitive=false, halign = Align.END, can_focus=true }; ok_button.add_css_class("suggested-action"); ok_button.clicked.connect(on_next_button_clicked); select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); Button cancel_button = new Button.with_label(_("Cancel")) { halign=Align.START }; cancel_button.clicked.connect(on_cancel); box.append(cancel_button); box.append(ok_button); wrap_box.append(box); // ok_button.has_default = true; } } private void setup_conference_details_view() { details_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; details_fragment.joined.connect(() => this.close()); Box wrap_box = new Box(Orientation.VERTICAL, 0); wrap_box.append(details_fragment); if (!Util.use_csd()) { Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 }; Button ok_button = new Button.with_label(_("Join")) { halign = Align.END, can_focus=true }; ok_button.add_css_class("suggested-action"); details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); details_fragment.ok_button = ok_button; Button cancel_button = new Button.with_label(_("Back")) { halign=Align.START }; cancel_button.clicked.connect(show_jid_add_view); box.append(cancel_button); box.append(ok_button); wrap_box.append(box); } stack.add_named(wrap_box, "details"); } private void set_ok_sensitive_from_select() { ok_button.sensitive = select_fragment.done; } private void set_ok_sensitive_from_details() { ok_button.sensitive = details_fragment.done; } private void on_next_button_clicked() { details_fragment.clear(); ListRow? row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row().get_child() as ListRow : null; ConferenceListRow? conference_row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row() as ConferenceListRow : null; if (conference_row != null) { details_fragment.account = conference_row.account; details_fragment.jid = conference_row.bookmark.jid.to_string(); details_fragment.nick = conference_row.bookmark.nick; if (conference_row.bookmark.password != null) details_fragment.password = conference_row.bookmark.password; ok_button.grab_focus(); } else if (row != null) { details_fragment.account = row.account; details_fragment.jid = row.jid.to_string(); } show_conference_details_view(); } private void on_cancel() { close(); } private void animate_window_resize(Widget widget) { int curr_height = get_size(Orientation.VERTICAL); var natural_size = Requisition(); widget.get_preferred_size(null, out natural_size); int difference = natural_size.height - curr_height; Timer timer = new Timer(); Timeout.add((int) (stack.transition_duration / 30), () => { ulong microsec; timer.elapsed(out microsec); ulong millisec = microsec / 1000; double partial = double.min(1, (double) millisec / stack.transition_duration); default_height = (int) (curr_height + difference * partial); return millisec < stack.transition_duration; }); } } } dino-0.4.3/main/src/ui/add_conversation/add_contact_dialog.vala0000644000000000000000000000407214452563620023260 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/add_contact_dialog.ui")] protected class AddContactDialog : Gtk.Dialog { public Account? account { get { return account_combobox.selected; } set { account_combobox.selected = value; } } public string jid { get { return jid_entry.text; } set { jid_entry.text = value; } } [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Entry alias_entry; private StreamInteractor stream_interactor; public AddContactDialog(StreamInteractor stream_interactor) { Object(use_header_bar : 1); this.stream_interactor = stream_interactor; account_combobox.initialize(stream_interactor); cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(on_ok_button_clicked); jid_entry.changed.connect(on_jid_entry_changed); } private void on_ok_button_clicked() { string? alias = alias_entry.text == "" ? null : alias_entry.text; try { Jid jid = new Jid(jid_entry.text); stream_interactor.get_module(RosterManager.IDENTITY).add_jid(account, jid, alias); stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); close(); } catch (InvalidJidError e) { warning("Tried to add contact with invalid Jid: %s", e.message); } } private void on_jid_entry_changed() { try { Jid parsed_jid = new Jid(jid_entry.text); ok_button. sensitive = parsed_jid != null && parsed_jid.resourcepart == null && stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, parsed_jid) == null; } catch (InvalidJidError e) { ok_button.sensitive = false; } } } } dino-0.4.3/main/src/ui/add_conversation/add_groupchat_dialog.vala0000644000000000000000000000503114452563620023615 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/add_groupchat_dialog.ui")] protected class AddGroupchatDialog : Gtk.Dialog { [GtkChild] private unowned Stack accounts_stack; [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Entry alias_entry; [GtkChild] private unowned Entry nick_entry; private StreamInteractor stream_interactor; private bool alias_entry_changed = false; public AddGroupchatDialog(StreamInteractor stream_interactor) { Object(use_header_bar : 1); this.stream_interactor = stream_interactor; ok_button.label = _("Add"); ok_button.add_css_class("suggested-action"); // TODO why doesn't it work in XML accounts_stack.set_visible_child_name("combobox"); account_combobox.initialize(stream_interactor); cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(on_ok_button_clicked); jid_entry.changed.connect(on_jid_key_release); nick_entry.changed.connect(check_ok); } private void on_jid_key_release() { check_ok(); if (!alias_entry_changed) { try { Jid parsed_jid = new Jid(jid_entry.text); alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text; } catch (InvalidJidError e) { alias_entry.text = jid_entry.text; } } } private void check_ok() { try { Jid parsed_jid = new Jid(jid_entry.text); ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; } catch (InvalidJidError e) { ok_button.sensitive = false; } } private void on_ok_button_clicked() { try { Conference conference = new Conference(); conference.jid = new Jid(jid_entry.text); conference.nick = nick_entry.text != "" ? nick_entry.text : null; conference.name = alias_entry.text; stream_interactor.get_module(MucManager.IDENTITY).add_bookmark(account_combobox.selected, conference); close(); } catch (InvalidJidError e) { warning("Ignoring invalid conference Jid: %s", e.message); } } } } dino-0.4.3/main/src/ui/add_conversation/conference_details_fragment.vala0000644000000000000000000002132214452563620025172 0ustar rootrootusing Gdk; using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Xep; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/conference_details_fragment.ui")] protected class ConferenceDetailsFragment : Box { public signal void joined(); public bool done { get; private set; } public Account account { owned get { return account_combobox.selected; } set { accounts_label.label = value.bare_jid.to_string(); account_combobox.selected = value; if (nick == null && value.alias != null) { nick = value.alias; } accounts_stack.set_visible_child_name("label"); } } public string jid { get { return jid_entry.text; } set { jid_label.label = value; jid_entry.text = value; jid_stack.set_visible_child_name("label"); check_if_done(); } } public string? nick { get { return nick_entry.text != "" ? nick_entry.text : null; } set { nick_label.label = value ?? ""; nick_entry.text = value ?? ""; nick_stack.set_visible_child_name("label"); check_if_done(); } } public string? password { get { return password_entry.text == "" ? null : password_entry.text; } set { password_label.label = value; password_entry.text = value; nick_stack.set_visible_child_name("label"); } } public bool fragment_active { get; set; default=true; } [GtkChild] private unowned Stack accounts_stack; [GtkChild] private unowned Button accounts_button; [GtkChild] private unowned Label accounts_label; [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Stack jid_stack; [GtkChild] private unowned Button jid_button; [GtkChild] private unowned Label jid_label; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Stack nick_stack; [GtkChild] private unowned Button nick_button; [GtkChild] private unowned Label nick_label; [GtkChild] private unowned Entry nick_entry; [GtkChild] private unowned Stack password_stack; [GtkChild] private unowned Button password_button; [GtkChild] private unowned Label password_label; [GtkChild] private unowned Label password_text_label; [GtkChild] private unowned Entry password_entry; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Button notification_button; [GtkChild] private unowned Label notification_label; private StreamInteractor stream_interactor; private Button ok_button_; public Button ok_button { get { return ok_button_; } set { if (value != null) { value.clicked.connect(() => { on_ok_button_clicked.begin(); }); ok_button_ = value; } } } public ConferenceDetailsFragment(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; account_combobox.initialize(stream_interactor); accounts_button.clicked.connect(() => { set_active_stack(accounts_stack); }); jid_button.clicked.connect(() => { set_active_stack(jid_stack); }); nick_button.clicked.connect(() => { set_active_stack(nick_stack); }); password_button.clicked.connect(() => { set_active_stack(password_stack); }); account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); }); accounts_label.label = account_combobox.selected.bare_jid.to_string(); // jid_entry.key_release_event.connect(on_jid_key_release_event); // nick_entry.key_release_event.connect(on_nick_key_release_event); // password_entry.key_release_event.connect(on_password_key_release_event); jid_entry.changed.connect(() => { check_if_done(); }); nick_entry.changed.connect(() => { check_if_done(); }); check_if_done(); notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); }); clear(); } public void clear() { jid = ""; nick = ""; password = ""; password_text_label.visible = false; password_stack.visible = false; notification_revealer.set_reveal_child(false); reset_editable(); } public void reset_editable() { jid_stack.set_visible_child_name("entry"); accounts_stack.set_visible_child_name("entry"); nick_stack.set_visible_child_name("entry"); password_stack.set_visible_child_name("entry"); } private async void on_ok_button_clicked() { if (!fragment_active) return; ok_button.label = _("Joining…"); ok_button.sensitive = false; string label_text = ""; try { Jid parsed_jid = new Jid(jid); Muc.JoinResult? join_result = yield stream_interactor.get_module(MucManager.IDENTITY).join(account, parsed_jid, nick, password); ok_button.label = _("Join"); ok_button.sensitive = true; if (join_result == null || join_result.nick != null) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, account, Conversation.Type.GROUPCHAT); Application app = GLib.Application.get_default() as Application; app.controller.select_conversation(conversation); joined(); return; } if (join_result.muc_error != null) { switch (join_result.muc_error) { case Muc.MucEnterError.PASSWORD_REQUIRED: label_text = _("Password required to enter room"); password_text_label.visible = true; password_stack.visible = true; break; case Muc.MucEnterError.BANNED: label_text = _("Banned from joining or creating conference"); break; case Muc.MucEnterError.ROOM_DOESNT_EXIST: label_text = _("Room does not exist"); break; case Muc.MucEnterError.CREATION_RESTRICTED: label_text = _("Not allowed to create room"); break; case Muc.MucEnterError.NOT_IN_MEMBER_LIST: label_text = _("Members-only room"); break; case Muc.MucEnterError.USE_RESERVED_ROOMNICK: case Muc.MucEnterError.NICK_CONFLICT: label_text = _("Choose a different nick"); break; case Muc.MucEnterError.OCCUPANT_LIMIT_REACHED: label_text = _("Too many occupants in room"); break; } } else if (join_result.stanza_error != null) { label_text = _("Could not connect to %s").printf((new Jid(jid)).domainpart); } } catch (InvalidJidError e) { label_text = _("Invalid address"); } notification_label.label = label_text; notification_revealer.set_reveal_child(true); } private void check_if_done() { try { Jid parsed_jid = new Jid(jid); done = parsed_jid.localpart != null && parsed_jid.resourcepart == null && nick != null; } catch (InvalidJidError e) { done = false; } } // private bool on_jid_key_release_event(EventKey event) { // jid_label.label = jid_entry.text; // if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); // return false; // } // // private bool on_nick_key_release_event(EventKey event) { // nick_label.label = nick_entry.text; // if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); // return false; // } // // private bool on_password_key_release_event(EventKey event) { // string filler = ""; // for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); // password_label.label = filler; // if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); // return false; // } private void set_active_stack(Stack stack) { stack.set_visible_child_name("entry"); if (stack != accounts_stack) accounts_stack.set_visible_child_name("label"); if (stack != jid_stack) jid_stack.set_visible_child_name("label"); if (stack != nick_stack) nick_stack.set_visible_child_name("label"); if (stack != password_stack) password_stack.set_visible_child_name("label"); } } } dino-0.4.3/main/src/ui/add_conversation/conference_list.vala0000644000000000000000000001047014452563620022637 0ustar rootrootusing Gee; using Gtk; using Xmpp; using Xmpp.Xep.Bookmarks; using Dino.Entities; namespace Dino.Ui { protected class ConferenceList { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; private ListBox list_box = new ListBox(); private HashMap> lists = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> widgets = new HashMap>(Account.hash_func, Account.equals_func); ulong bookmarks_updated_handler_id = -1; public ConferenceList(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; bookmarks_updated_handler_id = stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { lists[account] = conferences; refresh_conferences(); }); foreach (Account account in stream_interactor.get_accounts()) { stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks.begin(account, (_, res) => { Set? conferences = stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks.end(res); set_bookmarks(account, conferences); }); } stream_interactor.get_module(MucManager.IDENTITY).conference_added.connect(add_conference); stream_interactor.get_module(MucManager.IDENTITY).conference_removed.connect(remove_conference); } ~ConferenceList() { stream_interactor.get_module(MucManager.IDENTITY).disconnect(bookmarks_updated_handler_id); stream_interactor.get_module(MucManager.IDENTITY).conference_added.disconnect(add_conference); stream_interactor.get_module(MucManager.IDENTITY).conference_removed.disconnect(remove_conference); } private void add_conference(Account account, Conference conference) { if (!widgets.has_key(account)) { widgets[account] = new HashMap(Jid.hash_func, Jid.equals_func); } var widget = new ConferenceListRow(stream_interactor, conference, account); var list_box_row = new ListBoxRow(); list_box_row.set_child(widget); widgets[account][conference.jid] = list_box_row; list_box.append(list_box_row); } private void remove_conference(Account account, Jid jid) { if (widgets.has_key(account) && widgets[account].has_key(jid)) { list_box.remove(widgets[account][jid]); widgets[account].unset(jid); } } public void refresh_conferences() { foreach (Account account in widgets.keys) { var account_widgets_cpy = new HashMap(); account_widgets_cpy.set_all(widgets[account]); foreach (Jid jid in account_widgets_cpy.keys) { list_box.remove(widgets[account][jid]); } } foreach (Account account in lists.keys) { foreach (Conference conference in lists[account]) { add_conference(account, conference); } } } private void set_bookmarks(Account account, Set? conferences) { if (conferences == null) { lists.unset(account); } else { lists[account] = conferences; } refresh_conferences(); } public ListBox get_list_box() { return list_box; } } internal class ConferenceListRow : ListRow { public Conference bookmark; public ConferenceListRow(StreamInteractor stream_interactor, Conference bookmark, Account account) { this.jid = bookmark.jid; this.account = account; this.bookmark = bookmark; name_label.label = bookmark.name != null && bookmark.name != "" ? bookmark.name : bookmark.jid.to_string(); if (stream_interactor.get_accounts().size > 1) { via_label.label = "via " + account.bare_jid.to_string(); } else if (bookmark.name != null && bookmark.name != bookmark.jid.to_string()) { via_label.label = bookmark.jid.to_string(); } else { via_label.visible = false; } image.set_conversation(stream_interactor, new Conversation(jid, account, Conversation.Type.GROUPCHAT)); } } } dino-0.4.3/main/src/ui/add_conversation/list_row.vala0000644000000000000000000000330014452563620021331 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class ListRow : Widget { public Grid outer_grid; public AvatarImage image; public Label name_label; public Label via_label; public Jid? jid; public Account? account; construct { Builder builder = new Builder.from_resource("/im/dino/Dino/add_conversation/list_row.ui"); outer_grid = (Grid) builder.get_object("outer_grid"); image = (AvatarImage) builder.get_object("image"); name_label = (Label) builder.get_object("name_label"); via_label = (Label) builder.get_object("via_label"); this.layout_manager = new BinLayout(); outer_grid.set_parent(this); } public ListRow() {} public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) { this.jid = jid; this.account = account; Conversation conv = new Conversation(jid, account, Conversation.Type.CHAT); string display_name = Util.get_conversation_display_name(stream_interactor, conv); if (show_account && stream_interactor.get_accounts().size > 1) { via_label.label = @"via $(account.bare_jid)"; this.has_tooltip = Util.use_tooltips(); set_tooltip_text(Util.string_if_tooltips_active(jid.to_string())); } else if (display_name != jid.bare_jid.to_string()){ via_label.label = jid.bare_jid.to_string(); } else { via_label.visible = false; } name_label.label = display_name; image.set_conversation(stream_interactor, conv); } public override void dispose() { outer_grid.unparent(); } } } dino-0.4.3/main/src/ui/add_conversation/roster_list.vala0000644000000000000000000000516614452563620022054 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { protected class RosterList { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; private Gee.List accounts; private ulong[] handler_ids = new ulong[0]; private ListBox list_box = new ListBox(); private HashMap> rows = new HashMap>(Account.hash_func, Account.equals_func); public RosterList(StreamInteractor stream_interactor, Gee.List accounts) { this.stream_interactor = stream_interactor; this.accounts = accounts; handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => { if (accounts.contains(account)) { on_removed_roster_item(account, jid, roster_item); } }); handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect( (account, jid, roster_item) => { if (accounts.contains(account)) { on_updated_roster_item(account, jid, roster_item); } }); list_box.destroy.connect(() => { foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); }); foreach (Account a in accounts) fetch_roster_items(a); } private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { if (rows.has_key(account) && rows[account].has_key(jid)) { list_box.remove(rows[account][jid]); rows[account].unset(jid); } } private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) { on_removed_roster_item(account, jid, roster_item); ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1); ListBoxRow list_box_row = new ListBoxRow() { child=row }; rows[account][jid] = list_box_row; list_box.append(list_box_row); list_box.invalidate_sort(); list_box.invalidate_filter(); } private void fetch_roster_items(Account account) { rows[account] = new HashMap(Jid.hash_func, Jid.equals_func); foreach (Roster.Item roster_item in stream_interactor.get_module(RosterManager.IDENTITY).get_roster(account)) { on_updated_roster_item(account, roster_item.jid, roster_item); } } public ListBox get_list_box() { return list_box; } } } dino-0.4.3/main/src/ui/add_conversation/select_contact_dialog.vala0000644000000000000000000000747214452563620024016 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class SelectContactDialog : Gtk.Dialog { public signal void selected(Account account, Jid jid); public Button ok_button; private RosterList roster_list; private ListBox roster_list_box; private SelectJidFragment select_jid_fragment; private StreamInteractor stream_interactor; private Gee.List accounts; public SelectContactDialog(StreamInteractor stream_interactor, Gee.List accounts) { Object(use_header_bar : Util.use_csd() ? 1 : 0); modal = true; this.stream_interactor = stream_interactor; this.accounts = accounts; setup_view(); setup_headerbar(); } public void set_filter(string str) { select_jid_fragment.set_filter(str); } private void setup_headerbar() { Button cancel_button = new Button(); cancel_button.set_label(_("Cancel")); cancel_button.visible = true; ok_button = new Button(); ok_button.add_css_class("suggested-action"); ok_button.sensitive = false; ok_button.visible = true; if (Util.use_csd()) { HeaderBar header_bar = get_header_bar() as HeaderBar; header_bar.show_title_buttons = false; header_bar.pack_start(cancel_button); header_bar.pack_end(ok_button); } else { Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 }; cancel_button.halign = Align.START; ok_button.halign = Align.END; box.append(cancel_button); box.append(ok_button); get_content_area().append(box); } cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(() => { ListRow? selected_row = roster_list_box.get_selected_row() != null ? roster_list_box.get_selected_row().get_child() as ListRow : null; if (selected_row != null) selected(selected_row.account, selected_row.jid); close(); }); } private void setup_view() { roster_list = new RosterList(stream_interactor, accounts); roster_list_box = roster_list.get_list_box(); roster_list_box.row_activated.connect(() => { ok_button.clicked(); }); select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list_box, accounts); select_jid_fragment.add_jid.connect((row) => { AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); add_contact_dialog.set_transient_for(this); add_contact_dialog.present(); }); select_jid_fragment.remove_jid.connect((row) => { ListRow list_row = roster_list_box.get_selected_row().child as ListRow; stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid); }); select_jid_fragment.notify["done"].connect(() => { ok_button.sensitive = select_jid_fragment.done; }); get_content_area().append(select_jid_fragment); } } public class AddChatDialog : SelectContactDialog { public signal void added(Conversation conversation); public AddChatDialog(StreamInteractor stream_interactor, Gee.List accounts) { base(stream_interactor, accounts); title = _("Start Conversation"); ok_button.label = _("Start"); selected.connect((account, jid) => { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); added(conversation); }); } } } dino-0.4.3/main/src/ui/add_conversation/select_jid_fragment.vala0000644000000000000000000001056514452563620023472 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/select_jid_fragment.ui")] public class SelectJidFragment : Gtk.Box { public signal void add_jid(); public signal void remove_jid(ListRow row); public bool done { get { return list.get_selected_row() != null; } private set {} } [GtkChild] private unowned Entry entry; [GtkChild] private unowned Box box; [GtkChild] private unowned Button add_button; [GtkChild] private unowned Button remove_button; private StreamInteractor stream_interactor; private Gee.List accounts; private ArrayList added_rows = new ArrayList(); private ListBox list; private string[]? filter_values; public SelectJidFragment(StreamInteractor stream_interactor, ListBox list, Gee.List accounts) { this.stream_interactor = stream_interactor; this.list = list; this.accounts = accounts; list.activate_on_single_click = false; list.vexpand = true; box.append(list); list.set_sort_func(sort); list.set_filter_func(filter); list.set_header_func(header); list.row_selected.connect(check_buttons_active); list.row_selected.connect(() => { done = true; }); // just for notifying entry.changed.connect(() => { set_filter(entry.text); }); add_button.clicked.connect(() => { add_jid(); }); remove_button.clicked.connect(() => { var list_row = list.get_selected_row(); if (list_row == null) return; remove_jid(list_row.child as ListRow); }); } public void set_filter(string str) { if (entry.text != str) entry.text = str; foreach (Widget row in added_rows) list.remove(row); added_rows.clear(); filter_values = str == "" ? null : str.split(" "); list.invalidate_filter(); try { Jid parsed_jid = new Jid(str); if (parsed_jid != null && parsed_jid.localpart != null) { foreach (Account account in accounts) { var list_row = new Gtk.ListBoxRow(); list_row.set_child(new AddListRow(stream_interactor, parsed_jid, account)); list.append(list_row); added_rows.add(list_row); } } } catch (InvalidJidError ignored) { // Ignore } } private void check_buttons_active() { ListBoxRow? row = list.get_selected_row(); bool active = row != null && !row.get_type().is_a(typeof(AddListRow)); remove_button.sensitive = active; } private int sort(ListBoxRow row1, ListBoxRow row2) { AddListRow al1 = (row1 as AddListRow); AddListRow al2 = (row2 as AddListRow); if (al1 != null && al2 == null) { return -1; } else if (al2 != null && al1 == null) { return 1; } ListRow? c1 = (row1.child as ListRow); ListRow? c2 = (row2.child as ListRow); if (c1 != null && c2 != null) { return c1.name_label.label.collate(c2.name_label.label); } return 0; } private bool filter(ListBoxRow r) { ListRow? row = (r.child as ListRow); if (row == null) return true; if (filter_values != null) { foreach (string filter in filter_values) { if (!(row.name_label.label.down().contains(filter.down()) || row.jid.to_string().down().contains(filter.down()))) { return false; } } } return true; } private void header(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } private class AddListRow : ListRow { public AddListRow(StreamInteractor stream_interactor, Jid jid, Account account) { this.account = account; this.jid = jid; name_label.label = jid.to_string(); if (stream_interactor.get_accounts().size > 1) { via_label.label = account.bare_jid.to_string(); } else { via_label.visible = false; } image.set_text("?"); } } } } dino-0.4.3/main/src/ui/application.vala0000644000000000000000000003675414452563620016473 0ustar rootrootusing Gtk; using Dino.Entities; using Dino.Ui; using Xmpp; public class Dino.Ui.Application : Adw.Application, Dino.Application { private const string[] KEY_COMBINATION_QUIT = {"Q", null}; private const string[] KEY_COMBINATION_ADD_CHAT = {"T", null}; private const string[] KEY_COMBINATION_ADD_CONFERENCE = {"G", null}; private const string[] KEY_COMBINATION_LOOP_CONVERSATIONS = {"Tab", null}; private const string[] KEY_COMBINATION_LOOP_CONVERSATIONS_REV = {"Tab", null}; private MainWindow window; public MainWindowController controller; public Database db { get; set; } public Dino.Entities.Settings settings { get; set; } private Config config { get; set; } public StreamInteractor stream_interactor { get; set; } public Plugins.Registry plugin_registry { get; set; default = new Plugins.Registry(); } public SearchPathGenerator? search_path_generator { get; set; } internal static bool print_version = false; private const OptionEntry[] options = { { "version", 0, 0, OptionArg.NONE, ref print_version, "Display version number", null }, { null } }; public Application() throws Error { Object(application_id: "im.dino.Dino", flags: ApplicationFlags.HANDLES_OPEN); init(); Environment.set_application_name("Dino"); Window.set_default_icon_name("im.dino.Dino"); create_actions(); add_main_option_entries(options); startup.connect(() => { if (print_version) { print(@"Dino $(Dino.get_version())\n"); Process.exit(0); } NotificationEvents notification_events = stream_interactor.get_module(NotificationEvents.IDENTITY); get_notifications_dbus.begin((_, res) => { // It might take a bit to get the interface. NotificationEvents will queue any notifications in the meantime. try { DBusNotifications? dbus_notifications = get_notifications_dbus.end(res); if (dbus_notifications != null) { FreeDesktopNotifier free_desktop_notifier = new FreeDesktopNotifier(stream_interactor, dbus_notifications); notification_events.register_notification_provider.begin(free_desktop_notifier); } else { notification_events.register_notification_provider.begin(new GNotificationsNotifier(stream_interactor)); } } catch (Error e) { debug("Failed accessing fdo notification server: %s", e.message); } }); notification_events.notify_content_item.connect((content_item, conversation) => { // Set urgency hint also if (normal) notifications are disabled // Don't set urgency hint in GNOME, produces "Window is active" notification var desktop_env = Environment.get_variable("XDG_CURRENT_DESKTOP"); if (desktop_env == null || !desktop_env.down().contains("gnome")) { if (this.active_window != null) { // this.active_window.urgency_hint = true; } } }); }); activate.connect(() => { if (window == null) { controller = new MainWindowController(this, stream_interactor, db); config = new Config(db); window = new MainWindow(this, stream_interactor, db, config); controller.set_window(window); if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.hide_on_close = true; } window.present(); }); } public void handle_uri(string jid, string query, Gee.Map options) { switch (query) { case "join": show_join_muc_dialog(null, jid); break; case "message": Gee.List accounts = stream_interactor.get_accounts(); Jid parsed_jid = null; try { parsed_jid = new Jid(jid); } catch (InvalidJidError ignored) { // Ignored } if (accounts.size == 1 && parsed_jid != null) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, accounts[0], Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); controller.select_conversation(conversation); } else { AddChatDialog dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); dialog.set_filter(jid); dialog.set_transient_for(window); dialog.added.connect((conversation) => { controller.select_conversation(conversation); }); dialog.present(); } break; } } private void create_actions() { SimpleAction accounts_action = new SimpleAction("accounts", null); accounts_action.activate.connect(show_accounts_window); add_action(accounts_action); SimpleAction settings_action = new SimpleAction("settings", null); settings_action.activate.connect(show_settings_window); add_action(settings_action); SimpleAction about_action = new SimpleAction("about", null); about_action.activate.connect(show_about_window); add_action(about_action); SimpleAction quit_action = new SimpleAction("quit", null); quit_action.activate.connect(quit); add_action(quit_action); set_accels_for_action("app.quit", KEY_COMBINATION_QUIT); SimpleAction open_conversation_action = new SimpleAction("open-conversation", VariantType.INT32); open_conversation_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation != null) controller.select_conversation(conversation); Util.present_window(window); }); add_action(open_conversation_action); SimpleAction deny_subscription_action = new SimpleAction("deny-subscription", VariantType.INT32); deny_subscription_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; stream_interactor.get_module(PresenceManager.IDENTITY).deny_subscription(conversation.account, conversation.counterpart); }); add_action(deny_subscription_action); SimpleAction contacts_action = new SimpleAction("add_chat", null); contacts_action.activate.connect(() => { AddChatDialog add_chat_dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); add_chat_dialog.set_transient_for(window); add_chat_dialog.added.connect((conversation) => controller.select_conversation(conversation)); add_chat_dialog.present(); }); add_action(contacts_action); set_accels_for_action("app.add_chat", KEY_COMBINATION_ADD_CHAT); SimpleAction conference_action = new SimpleAction("add_conference", null); conference_action.activate.connect(() => { AddConferenceDialog add_conference_dialog = new AddConferenceDialog(stream_interactor); add_conference_dialog.set_transient_for(window); add_conference_dialog.present(); }); add_action(conference_action); set_accels_for_action("app.add_conference", KEY_COMBINATION_ADD_CONFERENCE); SimpleAction accept_muc_invite_action = new SimpleAction("open-muc-join", VariantType.INT32); accept_muc_invite_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; show_join_muc_dialog(conversation.account, conversation.counterpart.to_string()); }); add_action(accept_muc_invite_action); SimpleAction accept_voice_request_action = new SimpleAction("accept-voice-request", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.STRING})); accept_voice_request_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; string nick = variant.get_child_value(1).get_string(); stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, nick, "participant"); }); add_action(accept_voice_request_action); SimpleAction loop_conversations_action = new SimpleAction("loop_conversations", null); loop_conversations_action.activate.connect(() => { window.loop_conversations(false); }); add_action(loop_conversations_action); set_accels_for_action("app.loop_conversations", KEY_COMBINATION_LOOP_CONVERSATIONS); SimpleAction loop_conversations_bw_action = new SimpleAction("loop_conversations_bw", null); loop_conversations_bw_action.activate.connect(() => { window.loop_conversations(true); }); add_action(loop_conversations_bw_action); set_accels_for_action("app.loop_conversations_bw", KEY_COMBINATION_LOOP_CONVERSATIONS_REV); SimpleAction open_shortcuts_action = new SimpleAction("open_shortcuts", null); open_shortcuts_action.activate.connect((variant) => { Builder builder = new Builder.from_resource("/im/dino/Dino/shortcuts.ui"); ShortcutsWindow dialog = (ShortcutsWindow) builder.get_object("shortcuts-window"); if (!use_csd()) { dialog.set_titlebar(null); } dialog.title = _("Keyboard Shortcuts"); dialog.set_transient_for(get_active_window()); dialog.present(); }); add_action(open_shortcuts_action); SimpleAction accept_call_action = new SimpleAction("accept-call", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); accept_call_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; int call_id = variant.get_child_value(1).get_int32(); Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(call_id, conversation); CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call]; if (call_state == null) return; call_state.accept(); var call_window = new CallWindow(); call_window.controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.present(); }); add_action(accept_call_action); SimpleAction deny_call_action = new SimpleAction("reject-call", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); deny_call_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; int call_id = variant.get_child_value(1).get_int32(); Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(call_id, conversation); CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call]; if (call_state == null) return; call_state.reject(); }); add_action(deny_call_action); } public bool use_csd() { return Environment.get_variable("GTK_CSD") != "0"; } private void show_accounts_window() { ManageAccounts.Dialog dialog = new ManageAccounts.Dialog(stream_interactor, db); dialog.set_transient_for(get_active_window()); dialog.account_enabled.connect(add_connection); dialog.account_disabled.connect(remove_connection); dialog.present(); } private void show_settings_window() { SettingsDialog dialog = new SettingsDialog(); dialog.set_transient_for(get_active_window()); dialog.present(); } private void show_about_window() { string? version = Dino.get_version().strip().length == 0 ? null : Dino.get_version(); if (version != null && !version.contains("git")) { switch (version.substring(0, 3)) { case "0.2": version = @"$version - Mexican Caribbean Coral Reefs"; break; case "0.3": version = @"$version - Theikenmeer"; break; case "0.4": version = @"$version - Ilulissat"; break; } } #if Adw_1_2 Adw.AboutWindow about_window = new Adw.AboutWindow(); about_window.application_icon = "im.dino.Dino"; about_window.application_name = "Dino"; about_window.issue_url = "https://github.com/dino/dino/issues"; #else Gtk.AboutDialog about_window = new Gtk.AboutDialog(); about_window.logo_icon_name = "im.dino.Dino"; about_window.program_name = "Dino"; about_window.website_label = "dino.im"; #endif about_window.destroy_with_parent = true; about_window.transient_for = window; about_window.modal = true; about_window.title = _("About Dino"); about_window.version = version; about_window.website = "https://dino.im/"; about_window.copyright = "Copyright © 2016-2023 - Dino Team"; about_window.license_type = License.GPL_3_0; if (!use_csd()) { about_window.set_titlebar(null); } about_window.present(); } private void show_join_muc_dialog(Account? account, string jid) { Dialog dialog = new Dialog.with_buttons(_("Join Channel"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL); dialog.modal = true; Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button; ok_button.add_css_class("suggested-action"); ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; conference_fragment.jid = jid; if (account != null) { conference_fragment.account = account; } Box content_area = dialog.get_content_area(); content_area.append(conference_fragment); conference_fragment.joined.connect(() => { dialog.destroy(); }); dialog.response.connect((response_id) => { if (response_id == ResponseType.CANCEL) { dialog.destroy(); } }); dialog.present(); } } dino-0.4.3/main/src/ui/avatar_drawer.vala0000644000000000000000000001756614452563620017012 0ustar rootrootusing Cairo; using Gee; using Gdk; using Gtk; using Xmpp.Util; namespace Dino.Ui { public class AvatarDrawer { public const string GRAY = "555753"; private Gee.List tiles = new ArrayList(); private int height = 35; private int width = 35; private bool gray; private int base_factor = 1; private string font_family = "Sans"; public AvatarDrawer size(int height, int width = height) { this.height = height; this.width = width; return this; } public AvatarDrawer grayscale() { this.gray = true; return this; } public AvatarDrawer tile(Pixbuf? image, string? name, string? hex_color) { tiles.add(new AvatarTile(image, name, hex_color)); return this; } public AvatarDrawer plus() { tiles.add(new AvatarTile(null, "…", GRAY)); return this; } public AvatarDrawer scale(int base_factor) { this.base_factor = base_factor; return this; } public AvatarDrawer font(string font_family) { this.font_family = font_family; return this; } public ImageSurface draw_image_surface() { ImageSurface surface = new ImageSurface(Format.ARGB32, width, height); draw_on_context(new Context(surface)); return surface; } public void draw_on_context(Cairo.Context ctx) { double radius = 3 * base_factor; double degrees = Math.PI / 180.0; ctx.new_sub_path(); ctx.arc(width - radius, radius, radius, -90 * degrees, 0 * degrees); ctx.arc(width - radius, height - radius, radius, 0 * degrees, 90 * degrees); ctx.arc(radius, height - radius, radius, 90 * degrees, 180 * degrees); ctx.arc(radius, radius, radius, 180 * degrees, 270 * degrees); ctx.close_path(); ctx.clip(); if (this.tiles.size == 4) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width - 1, height - 1, 2 * base_factor), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width - 1, height - 1, 2 * base_factor), width + 1, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 2, width - 1, height - 1, 2 * base_factor), 0, height + 1); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 3, width - 1, height - 1, 2 * base_factor), width + 1, height + 1); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.tiles.size == 3) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width - 1, height - 1, 2 * base_factor), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width - 1, height * 2, 2 * base_factor), width + 1, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 2, width - 1, height - 1, 2 * base_factor), 0, height + 1); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.tiles.size == 2) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width - 1, height * 2, 2 * base_factor), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width - 1, height * 2, 2 * base_factor), width + 1, 0); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.tiles.size == 1) { ctx.set_source_surface(sub_surface_idx(ctx, 0, width, height, base_factor), 0, 0); ctx.paint(); } else if (this.tiles.size == 0) { ctx.set_source_surface(sub_surface_idx(ctx, -1, width, height, base_factor), 0, 0); ctx.paint(); } if (gray) { // convert to greyscale ctx.set_operator(Cairo.Operator.HSL_COLOR); ctx.set_source_rgb(1, 1, 1); ctx.rectangle(0, 0, width, height); ctx.fill(); // make the visible part more light ctx.set_operator(Cairo.Operator.ATOP); ctx.set_source_rgba(1, 1, 1, 0.7); ctx.rectangle(0, 0, width, height); ctx.fill(); } ctx.set_source_rgb(0, 0, 0); } private Cairo.Surface sub_surface_idx(Cairo.Context ctx, int idx, int width, int height, int font_factor = 1) { Gdk.Pixbuf? avatar = idx >= 0 ? tiles[idx].image : null; string? name = idx >= 0 ? tiles[idx].name : ""; string hex_color = !gray && idx >= 0 ? tiles[idx].hex_color : GRAY; return sub_surface(ctx, font_family, avatar, name, hex_color, width, height, font_factor); } private static Cairo.Surface sub_surface(Cairo.Context ctx, string font_family, Gdk.Pixbuf? avatar, string? name, string? hex_color, int width, int height, int font_factor = 1) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height); Cairo.Context bufctx = new Cairo.Context(buffer); if (avatar == null) { set_source_hex_color(bufctx, hex_color ?? GRAY); bufctx.rectangle(0, 0, width, height); bufctx.fill(); string text = name == null ? "…" : name.get_char(0).toupper().to_string(); bufctx.select_font_face(font_family, Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); bufctx.set_font_size(width / font_factor < 40 ? font_factor * 17 : font_factor * 25); Cairo.TextExtents extents; bufctx.text_extents(text, out extents); double x_pos = width/2 - (extents.width/2 + extents.x_bearing); double y_pos = height/2 - (extents.height/2 + extents.y_bearing); bufctx.move_to(x_pos, y_pos); bufctx.set_source_rgba(1, 1, 1, 1); bufctx.show_text(text); } else { double w_scale = (double) width / avatar.width; double h_scale = (double) height / avatar.height; double scale = double.max(w_scale, h_scale); bufctx.scale(scale, scale); double x_off = 0, y_off = 0; if (scale == h_scale) { x_off = (width / scale - avatar.width) / 2.0; } else { y_off = (height / scale - avatar.height) / 2.0; } Gdk.cairo_set_source_pixbuf(bufctx, avatar, x_off, y_off); bufctx.get_source().set_filter(Cairo.Filter.BEST); bufctx.paint(); } return buffer; } private static void set_source_hex_color(Cairo.Context ctx, string hex_color) { ctx.set_source_rgba((double) from_hex(hex_color.substring(0, 2)) / 255, (double) from_hex(hex_color.substring(2, 2)) / 255, (double) from_hex(hex_color.substring(4, 2)) / 255, hex_color.length > 6 ? (double) from_hex(hex_color.substring(6, 2)) / 255 : 1); } } private class AvatarTile { public Pixbuf? image { get; private set; } public string? name { get; private set; } public string? hex_color { get; private set; } public AvatarTile(Pixbuf? image, string? name, string? hex_color) { this.image = image; this.name = name; this.hex_color = hex_color; } } }dino-0.4.3/main/src/ui/avatar_generator.vala0000644000000000000000000000000014452563620017463 0ustar rootrootdino-0.4.3/main/src/ui/avatar_image.vala0000644000000000000000000002716614452563620016605 0ustar rootrootusing Gtk; using Dino.Entities; using Xmpp; using Xmpp.Util; namespace Dino.Ui { public class AvatarImage : Widget { public int height { get; set; default = 35; } public int width { get; set; default = 35; } public bool allow_gray { get; set; default = true; } public bool force_gray { get; set; default = false; } public StreamInteractor? stream_interactor { get; set; } public AvatarManager? avatar_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(AvatarManager.IDENTITY); } } public MucManager? muc_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(MucManager.IDENTITY); } } public PresenceManager? presence_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(PresenceManager.IDENTITY); } } public ConnectionManager? connection_manager { owned get { return stream_interactor == null ? null : stream_interactor.connection_manager; } } public Account account { get { return conversation.account; } } private AvatarDrawer? drawer; private Conversation conversation; private Jid[] jids; private Cairo.ImageSurface? cached_surface; private static int8 use_image_surface = -1; public AvatarImage() { can_focus = false; add_css_class("avatar"); } public override void dispose() { base.dispose(); drawer = null; cached_surface = null; disconnect_stream_interactor(); } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (orientation == Orientation.HORIZONTAL) { minimum = width; natural = width; } else { minimum = height; natural = height; } minimum_baseline = natural_baseline = -1; } public override void snapshot(Snapshot snapshot) { Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, width, height)); draw(context); } public bool draw(Cairo.Context ctx_in) { Cairo.Context ctx = ctx_in; int width = this.width, height = this.height, base_factor = 1; if (use_image_surface == -1) { // TODO: detect if we have to buffer in image surface use_image_surface = 1; } if (use_image_surface == 1) { ctx_in.scale(1f / scale_factor, 1f / scale_factor); if (cached_surface != null) { ctx_in.set_source_surface(cached_surface, 0, 0); ctx_in.paint(); ctx_in.set_source_rgb(0, 0, 0); return true; } width *= scale_factor; height *= scale_factor; base_factor *= scale_factor; cached_surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width, height); ctx = new Cairo.Context(cached_surface); } AvatarDrawer drawer = this.drawer; Jid[] jids = this.jids; if (drawer == null && jids.length == 0) { switch (conversation.type_) { case Conversation.Type.CHAT: case Conversation.Type.GROUPCHAT_PM: // In direct chats or group chats, conversation avatar is same as counterpart avatar jids = { conversation.counterpart }; break; case Conversation.Type.GROUPCHAT: string user_color = Util.get_avatar_hex_color(stream_interactor, account, conversation.counterpart, conversation); if (avatar_manager.has_avatar_cached(account, conversation.counterpart)) { drawer = new AvatarDrawer().tile(avatar_manager.get_cached_avatar(account, conversation.counterpart), "#", user_color); if (force_gray || allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); } else { Gee.List? occupants = muc_manager.get_other_offline_members(conversation.counterpart, account); if (muc_manager.is_private_room(account, conversation.counterpart) && occupants != null && occupants.size > 0) { jids = occupants.to_array(); } else { drawer = new AvatarDrawer().tile(null, "#", user_color); if (force_gray || allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); } try_load_avatar_async(conversation.counterpart); } break; } } if (drawer == null && jids.length > 0) { drawer = new AvatarDrawer(); for (int i = 0; i < (jids.length <= 4 ? jids.length : 3); i++) { Jid avatar_jid = jids[i]; Jid? real_avatar_jid = null; if (conversation.type_ != Conversation.Type.CHAT && avatar_jid.equals_bare(conversation.counterpart) && muc_manager.is_private_room(account, conversation.counterpart.bare_jid)) { // In private room, consider real jid real_avatar_jid = muc_manager.get_real_jid(avatar_jid, account) ?? avatar_jid; } string display_name = Util.get_participant_display_name(stream_interactor, conversation, jids[i]); string user_color = Util.get_avatar_hex_color(stream_interactor, account, jids[i], conversation); if (avatar_manager.has_avatar_cached(account, avatar_jid)) { drawer.tile(avatar_manager.get_cached_avatar(account, avatar_jid), display_name, user_color); } else if (real_avatar_jid != null && avatar_manager.has_avatar_cached(account, real_avatar_jid)) { drawer.tile(avatar_manager.get_cached_avatar(account, real_avatar_jid), display_name, user_color); } else { drawer.tile(null, display_name, user_color); try_load_avatar_async(avatar_jid); if (real_avatar_jid != null) try_load_avatar_async(real_avatar_jid); } } if (jids.length > 4) { drawer.plus(); } if (force_gray || allow_gray && (!is_self_online() || !is_counterpart_online())) drawer.grayscale(); } if (drawer == null) return false; drawer.size(height, width) .scale(base_factor) .font(get_pango_context().get_font_description().get_family()) .draw_on_context(ctx); if (use_image_surface == 1) { ctx_in.set_source_surface(ctx.get_target(), 0, 0); ctx_in.paint(); ctx_in.set_source_rgb(0, 0, 0); } return true; } private void try_load_avatar_async(Jid jid) { if (avatar_manager.has_avatar(account, jid)) { avatar_manager.get_avatar.begin(account, jid, (_, res) => { var avatar = avatar_manager.get_avatar.end(res); if (avatar != null) force_redraw(); }); } } private void force_redraw() { this.cached_surface = null; queue_draw(); } private void disconnect_stream_interactor() { if (stream_interactor != null) { presence_manager.show_received.disconnect(on_show_received); presence_manager.received_offline_presence.disconnect(on_show_received); avatar_manager.received_avatar.disconnect(on_received_avatar); stream_interactor.connection_manager.connection_state_changed.disconnect(on_connection_changed); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.disconnect(on_roster_updated); muc_manager.private_room_occupant_updated.disconnect(on_private_room_occupant_updated); muc_manager.room_info_updated.disconnect(on_room_info_updated); stream_interactor = null; } } private void on_show_received(Jid jid, Account account) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void on_received_avatar(Jid jid, Account account) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void update_avatar_if_jid(Jid jid) { if (jid.equals_bare(this.conversation.counterpart)) { force_redraw(); return; } foreach (Jid ours in this.jids) { if (jid.equals_bare(ours)) { force_redraw(); return; } } } private void on_connection_changed(Account account, ConnectionManager.ConnectionState state) { if (!account.equals(this.account)) return; force_redraw(); } private void on_roster_updated(Account account, Jid jid, Roster.Item roster_item) { if (!account.equals(this.account)) return; update_avatar_if_jid(jid); } private void on_private_room_occupant_updated(Account account, Jid room, Jid occupant) { if (!account.equals(this.account)) return; update_avatar_if_jid(room); } private void on_room_info_updated(Account account, Jid muc_jid) { if (!account.equals(this.account)) return; update_avatar_if_jid(muc_jid); } private bool is_self_online() { if (connection_manager != null) { return connection_manager.get_state(account) == ConnectionManager.ConnectionState.CONNECTED; } return false; } private bool is_counterpart_online() { return presence_manager.get_full_jids(conversation.counterpart, account) != null; } public void set_conversation(StreamInteractor stream_interactor, Conversation conversation) { set_avatar(stream_interactor, conversation, new Jid[0]); } public void set_conversation_participant(StreamInteractor stream_interactor, Conversation conversation, Jid sub_jid) { set_avatar(stream_interactor, conversation, new Jid[] {sub_jid}); } public void set_conversation_participants(StreamInteractor stream_interactor, Conversation conversation, Jid[] sub_jids) { set_avatar(stream_interactor, conversation, sub_jids); } private void set_avatar(StreamInteractor stream_interactor, Conversation conversation, Jid[] jids) { if (this.stream_interactor != null && stream_interactor != this.stream_interactor) { disconnect_stream_interactor(); } if (this.stream_interactor != stream_interactor) { this.stream_interactor = stream_interactor; presence_manager.show_received.connect(on_show_received); presence_manager.received_offline_presence.connect(on_show_received); stream_interactor.get_module(AvatarManager.IDENTITY).received_avatar.connect(on_received_avatar); stream_interactor.connection_manager.connection_state_changed.connect(on_connection_changed); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect(on_roster_updated); muc_manager.private_room_occupant_updated.connect(on_private_room_occupant_updated); muc_manager.room_info_updated.connect(on_room_info_updated); } this.cached_surface = null; this.conversation = conversation; this.jids = jids; force_redraw(); } public void set_text(string text, bool gray = true) { disconnect_stream_interactor(); this.drawer = new AvatarDrawer().tile(null, text, null); if (gray) drawer.grayscale(); force_redraw(); } } } dino-0.4.3/main/src/ui/call_window/0000755000000000000000000000000014452563620015606 5ustar rootrootdino-0.4.3/main/src/ui/call_window/audio_settings_popover.vala0000644000000000000000000001540114452563620023247 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { public signal void microphone_selected(Plugins.MediaDevice device); public signal void speaker_selected(Plugins.MediaDevice device); public Plugins.MediaDevice? current_microphone_device { get; set; } public Plugins.MediaDevice? current_speaker_device { get; set; } private HashMap row_microphone_device = new HashMap(); private HashMap row_speaker_device = new HashMap(); public AudioSettingsPopover() { Box box = new Box(Orientation.VERTICAL, 15); box.append(create_microphone_box()); box.append(create_speaker_box()); this.set_child(box); } private Widget create_microphone_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("audio", false); Box micro_box = new Box(Orientation.VERTICAL, 10); micro_box.append(new Label("" + _("Microphones") + "") { use_markup=true, xalign=0, can_focus=true /* grab initial focus*/ }); if (devices.size == 0) { micro_box.append(new Label(_("No microphone found."))); } else { ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; micro_list_box.set_header_func(listbox_header_func); Frame micro_frame = new Frame(null); micro_frame.set_child(micro_list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_microphone_device == null || current_microphone_device.id != device.id) { image.opacity = 0; } this.notify["current-microphone-device"].connect(() => { if (current_microphone_device == null || current_microphone_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0); label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); micro_list_box.append(list_box_row); row_microphone_device[list_box_row] = device; } micro_list_box.row_activated.connect((row) => { if (!row_microphone_device.has_key(row)) return; microphone_selected(row_microphone_device[row]); micro_list_box.unselect_row(row); }); micro_box.append(micro_frame); } return micro_box; } private Widget create_speaker_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("audio", true); Box speaker_box = new Box(Orientation.VERTICAL, 10); speaker_box.append(new Label("" + _("Speakers") +"") { use_markup=true, xalign=0 }); if (devices.size == 0) { speaker_box.append(new Label(_("No speaker found."))); } else { ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; speaker_list_box.set_header_func(listbox_header_func); speaker_list_box.row_selected.connect((row) => { }); Frame speaker_frame = new Frame(null); speaker_frame.set_child(speaker_list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_speaker_device == null || current_speaker_device.id != device.id) { image.opacity = 0; } this.notify["current-speaker-device"].connect(() => { if (current_speaker_device == null || current_speaker_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); speaker_list_box.append(list_box_row); row_speaker_device[list_box_row] = device; } speaker_list_box.row_activated.connect((row) => { if (!row_speaker_device.has_key(row)) return; speaker_selected(row_speaker_device[row]); speaker_list_box.unselect_row(row); }); speaker_box.append(speaker_frame); } return speaker_box; } private void listbox_header_func(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } }dino-0.4.3/main/src/ui/call_window/call_bottom_bar.vala0000644000000000000000000001457514452563620021612 0ustar rootrootusing Dino.Entities; using Gtk; using Pango; public class Dino.Ui.CallBottomBar : Gtk.Box { public signal void hang_up(); public bool audio_enabled { get; set; } public bool video_enabled { get; set; } public string counterpart_display_name { get; set; } private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; private Overlay audio_button_overlay = new Overlay(); private Image audio_image = new Image() { pixel_size=22 }; private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; public AudioSettingsPopover? audio_settings_popover; private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; private Overlay video_button_overlay = new Overlay(); private Image video_image = new Image() { pixel_size=22 }; private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; public VideoSettingsPopover? video_settings_popover; private Label label = new Label("") { halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true }; private Stack stack = new Stack(); public CallBottomBar() { Object(orientation:Orientation.HORIZONTAL, spacing:0); Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin_bottom=20, margin_top=20, halign=Align.CENTER, hexpand=true }; audio_button.set_child(audio_image); audio_button.add_css_class("call-button"); audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; }); audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button audio_button_overlay.set_child(audio_button); audio_button_overlay.add_overlay(audio_settings_button); Util.menu_button_set_icon_with_size(audio_settings_button, "go-up-symbolic", 10); audio_settings_button.add_css_class("call-mediadevice-settings-button"); main_buttons.append(audio_button_overlay); video_button.set_child(video_image); video_button.add_css_class("call-button"); video_button.clicked.connect(() => { video_enabled = !video_enabled; }); video_button.margin_end = video_button.margin_bottom = 5; video_button_overlay.set_child(video_button); video_button_overlay.add_overlay(video_settings_button); Util.menu_button_set_icon_with_size(video_settings_button, "go-up-symbolic", 10); video_settings_button.add_css_class("call-mediadevice-settings-button"); main_buttons.append(video_button_overlay); Button button_hang = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; button_hang.set_child(new Image() { icon_name="dino-phone-hangup-symbolic", pixel_size=22 }); button_hang.add_css_class("call-button"); button_hang.add_css_class("destructive-action"); button_hang.clicked.connect(() => hang_up()); main_buttons.append(button_hang); label.add_css_class("text-no-controls"); stack.add_named(main_buttons, "control-buttons"); stack.add_named(label, "label"); this.append(stack); this.notify["audio-enabled"].connect(on_audio_enabled_changed); this.notify["video-enabled"].connect(on_video_enabled_changed); audio_enabled = true; video_enabled = false; on_audio_enabled_changed(); on_video_enabled_changed(); this.add_css_class("call-bottom-bar"); } public AudioSettingsPopover? show_audio_device_choices(bool show) { audio_settings_button.visible = show; if (audio_settings_popover != null) audio_settings_popover.visible = false; if (!show) return null; audio_settings_popover = new AudioSettingsPopover(); audio_settings_button.popover = audio_settings_popover; audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.popdown(); }); audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.popdown(); }); return audio_settings_popover; } public void show_audio_device_error() { audio_settings_button.set_icon_name("dialog-warning-symbolic"); Util.force_error_color(audio_settings_button); } public VideoSettingsPopover? show_video_device_choices(bool show) { video_settings_button.visible = show; if (video_settings_popover != null) video_settings_popover.visible = false; if (!show) return null; video_settings_popover = new VideoSettingsPopover(); video_settings_button.popover = video_settings_popover; video_settings_popover.camera_selected.connect(() => { video_settings_button.popdown(); }); return video_settings_popover; } public void show_video_device_error() { video_settings_button.set_icon_name("dialog-warning-symbolic"); Util.force_error_color(video_settings_button); } public void on_audio_enabled_changed() { if (audio_enabled) { audio_image.icon_name = "dino-microphone-symbolic"; audio_button.add_css_class("white-button"); audio_button.remove_css_class("transparent-white-button"); } else { audio_image.icon_name = "dino-microphone-off-symbolic"; audio_button.remove_css_class("white-button"); audio_button.add_css_class("transparent-white-button"); } } public void on_video_enabled_changed() { if (video_enabled) { video_image.icon_name = "dino-video-symbolic"; video_button.add_css_class("white-button"); video_button.remove_css_class("transparent-white-button"); } else { video_image.icon_name = "dino-video-off-symbolic"; video_button.remove_css_class("white-button"); video_button.add_css_class("transparent-white-button"); } } public void show_counterpart_ended(string text) { stack.set_visible_child_name("label"); label.label = text; } public bool is_menu_active() { return (video_settings_button.popover != null && video_settings_button.popover.visible) || (audio_settings_button.popover != null && audio_settings_button.popover.visible); } }dino-0.4.3/main/src/ui/call_window/call_connection_details_window.vala0000644000000000000000000001132314452563620024701 0ustar rootrootusing Gtk; namespace Dino.Ui { public class CallConnectionDetailsWindow : Gtk.Window { public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER }; private bool video_added = false; private CallContentDetails audio_details = new CallContentDetails("Audio"); private CallContentDetails video_details = new CallContentDetails("Video"); public CallConnectionDetailsWindow() { box.append(audio_details); box.append(video_details); set_child(box); } public void update_content(PeerInfo peer_info) { if (peer_info.audio != null) { audio_details.update_content(peer_info.audio); } if (peer_info.video != null) { add_video_widgets(); video_details.update_content(peer_info.video); } } private void add_video_widgets() { if (video_added) return; video_details.visible = true; video_added = true; } } public class CallContentDetails : Gtk.Grid { public Label rtp_title = new Label("RTP") { xalign=0 }; public Label rtcp_title = new Label("RTCP") { xalign=0 }; public Label target_recv_title = new Label("Target receive bitrate") { xalign=0 }; public Label target_send_title = new Label("Target send bitrate") { xalign=0 }; public Label rtp_ready = new Label("?") { xalign=0 }; public Label rtcp_ready = new Label("?") { xalign=0 }; public Label sent_bps = new Label("?") { use_markup=true, xalign=0 }; public Label recv_bps = new Label("?") { use_markup=true, xalign=0 }; public Label codec = new Label("?") { xalign=0 }; public Label target_receive_bitrate = new Label("n/a") { use_markup=true, xalign=0 }; public Label target_send_bitrate = new Label("n/a") { use_markup=true, xalign=0 }; private PeerContentInfo? prev_info = null; private int row_at = 0; public CallContentDetails(string headline) { attach(new Label("%s".printf(headline)) { use_markup=true, xalign=0 }, 0, row_at++, 1, 1); attach(rtp_title, 0, row_at, 1, 1); attach(rtp_ready, 1, row_at++, 1, 1); attach(rtcp_title, 0, row_at, 1, 1); attach(rtcp_ready, 1, row_at++, 1, 1); put_row("Sent"); attach(sent_bps, 1, row_at++, 1, 1); put_row("Received"); attach(recv_bps, 1, row_at++, 1, 1); put_row("Codec"); attach(codec, 1, row_at++, 1, 1); attach(target_recv_title, 0, row_at, 1, 1); attach(target_receive_bitrate, 1, row_at++, 1, 1); attach(target_send_title, 0, row_at, 1, 1); attach(target_send_bitrate, 1, row_at++, 1, 1); this.column_spacing = 5; } public void update_content(PeerContentInfo info) { if (!info.rtp_ready) { rtp_ready.visible = rtcp_ready.visible = true; rtp_title.visible = rtcp_title.visible = true; rtp_ready.label = info.rtp_ready.to_string(); rtcp_ready.label = info.rtcp_ready.to_string(); } else { rtp_ready.visible = rtcp_ready.visible = false; rtp_title.visible = rtcp_title.visible = false; } if (info.target_send_bytes != -1) { target_receive_bitrate.visible = target_send_bitrate.visible = true; target_recv_title.visible = target_send_title.visible = true; target_receive_bitrate.label = "%u kbps".printf(info.target_receive_bytes); target_send_bitrate.label = "%u kbps".printf(info.target_send_bytes); } else { target_receive_bitrate.visible = target_send_bitrate.visible = false; target_recv_title.visible = target_send_title.visible = false; } codec.label = info.codec + " " + info.clockrate.to_string(); if (prev_info != null) { ulong audio_sent_kbps = (info.bytes_sent - prev_info.bytes_sent) * 8 / 1000; sent_bps.label = "%lu kbps".printf(audio_sent_kbps); ulong audio_recv_kbps = (info.bytes_received - prev_info.bytes_received) * 8 / 1000; recv_bps.label = "%lu kbps".printf(audio_recv_kbps); } prev_info = info; } private void put_row(string label) { attach(new Label(label) { xalign=0 }, 0, row_at, 1, 1); } } } dino-0.4.3/main/src/ui/call_window/call_encryption_button.vala0000644000000000000000000000657614452563620023251 0ustar rootrootusing Dino.Entities; using Gtk; using Pango; public class Dino.Ui.CallEncryptionButtonController : Object { private bool has_been_set = false; public bool controls_active { get; set; default=false; } public MenuButton button; public CallEncryptionButtonController(MenuButton button) { this.button = button; button.opacity = 0; // button.set_popover(popover); button.notify["controls-active"].connect(update_opacity); } public void set_icon(bool encrypted, string? icon_name) { if (encrypted) { button.icon_name = icon_name ?? "changes-prevent-symbolic"; button.remove_css_class("unencrypted"); } else { button.icon_name = icon_name ?? "changes-allow-symbolic"; button.add_css_class("unencrypted"); } has_been_set = true; update_opacity(); } public void set_info(string? title, bool show_keys, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) { Popover popover = new Popover(); button.set_popover(popover); if (audio_encryption == null) { popover.set_child(new Label("This call is unencrypted.") ); return; } if (title != null && !show_keys) { popover.set_child(new Label(title) { use_markup=true } ); return; } Box box = new Box(Orientation.VERTICAL, 10); box.append(new Label("%s".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0 }); if (video_encryption == null) { box.append(create_media_encryption_grid(audio_encryption)); } else { box.append(new Label("Audio") { use_markup=true, xalign=0 }); box.append(create_media_encryption_grid(audio_encryption)); box.append(new Label("Video") { use_markup=true, xalign=0 }); box.append(create_media_encryption_grid(video_encryption)); } popover.set_child(box); } public void update_opacity() { button.opacity = controls_active && has_been_set ? 1 : 0; } private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) { Grid ret = new Grid() { row_spacing=3, column_spacing=5 }; if (encryption.peer_key.length > 0) { ret.attach(new Label("Peer call key") { xalign=0 }, 1, 2, 1, 1); ret.attach(new Label("" + format_fingerprint(encryption.peer_key) + "") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 2, 1, 1); } if (encryption.our_key.length > 0) { ret.attach(new Label("Your call key") { xalign=0 }, 1, 3, 1, 1); ret.attach(new Label("" + format_fingerprint(encryption.our_key) + "") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 3, 1, 1); } return ret; } private string format_fingerprint(uint8[] fingerprint) { var sb = new StringBuilder(); for (int i = 0; i < fingerprint.length; i++) { sb.append("%02x".printf(fingerprint[i])); if (i < fingerprint.length - 1) { sb.append(":"); } } return sb.str; } }dino-0.4.3/main/src/ui/call_window/call_window.vala0000644000000000000000000003022114452563620020753 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; using Gtk; namespace Dino.Ui { public class CallWindow : Gtk.Window { public signal void menu_dump_dot(); public CallWindowController controller; public Overlay overlay = new Overlay(); public Grid grid = new Grid(); public CallBottomBar bottom_bar = new CallBottomBar(); public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200 }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, opacity=0.0 }; public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, reveal_child=false }; public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END }; private Widget? own_video = null; private HashMap participant_widgets = new HashMap(); private ArrayList participants = new ArrayList(); private EventControllerFocus this_focus_events = new EventControllerFocus(); private GestureClick this_gesture_events = new GestureClick() { touch_only=true, propagation_phase=Gtk.PropagationPhase.CAPTURE }; private EventControllerMotion this_motion_events = new EventControllerMotion(); private double latest_motion_x = -1; private double latest_motion_y = -1; private const double MOTION_RELEVANCE_THRESHOLD = 2; private int own_video_width = 150; private int own_video_height = 100; private bool hide_control_elements = false; private uint hide_control_handler = 0; public bool controls_active { get; set; default=true; } construct { header_bar.add_css_class("call-header-bar"); header_bar.title_widget = new Box(Orientation.VERTICAL, 0); // header_bar.spacing = 0; header_bar_revealer.set_child(header_bar); bottom_bar_revealer.set_child(bottom_bar); own_video_box.add_css_class("own-video"); this.add_css_class("dino-call-window"); overlay.set_child(grid); overlay.add_overlay(own_video_box); overlay.add_overlay(bottom_bar_revealer); overlay.add_overlay(header_bar_revealer); overlay.get_child_position.connect(on_get_child_position); set_child(overlay); } public CallWindow() { this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE); ((Widget) this).add_controller(this_motion_events); this_motion_events.motion.connect((x, y) => { if ((latest_motion_x - x).abs() <= MOTION_RELEVANCE_THRESHOLD && (latest_motion_y - y).abs() <= MOTION_RELEVANCE_THRESHOLD) return; latest_motion_x = x; latest_motion_y = y; reveal_control_elements(); }); ((Widget) this).add_controller(this_focus_events); this_focus_events.enter.connect(reveal_control_elements); this_focus_events.leave.connect(reveal_control_elements); ((Widget) this).add_controller(this_gesture_events); this_gesture_events.pressed.connect_after(reveal_control_elements); this.notify["default-width"].connect(reveal_control_elements); this.notify["default-height"].connect(reveal_control_elements); this.notify["default-width"].connect(reposition_participant_widgets); this.notify["default-height"].connect(reposition_participant_widgets); this.set_titlebar(new OutsideHeaderBar(this.header_bar)); reveal_control_elements(); } public void add_participant(string participant, ParticipantWidget participant_widget) { participant_widget.visible = true; this.bind_property("controls-active", participant_widget, "controls-active", BindingFlags.SYNC_CREATE); this.bind_property("controls-active", participant_widget.encryption_button_controller, "controls-active", BindingFlags.SYNC_CREATE); participants.add(participant); participant_widgets[participant] = participant_widget; grid.attach(participant_widget, 0, 0); reposition_participant_widgets(); } public void remove_participant(string participant) { participants.remove(participant); grid.remove(participant_widgets[participant]); participant_widgets.unset(participant); reposition_participant_widgets(); } public void set_video(string participant, Widget widget) { participant_widgets[participant].set_video(widget); hide_control_elements = true; timeout_hide_control_elements(); } public void set_placeholder(string participant, Conversation? conversation, StreamInteractor stream_interactor) { participant_widgets[participant].set_placeholder(conversation, stream_interactor); hide_control_elements = false; foreach (ParticipantWidget participant_widget in participant_widgets.values) { if (participant_widget.shows_video) { hide_control_elements = true; } } if (!hide_control_elements) { reveal_control_elements(); } } private void reposition_participant_widgets() { int width = get_size(Orientation.HORIZONTAL); int height = get_size(Orientation.VERTICAL); reposition_participant_widgets_rec(participants, width, height, 0, 0, 0, 0); } private void reposition_participant_widgets_rec(ArrayList participants, int width, int height, int margin_top, int margin_right, int margin_bottom, int margin_left) { if (participants.size == 0) return; if (participants.size == 1) { participant_widgets[participants[0]].margin_top = margin_top; participant_widgets[participants[0]].margin_end = margin_right; participant_widgets[participants[0]].margin_bottom = margin_bottom; participant_widgets[participants[0]].margin_start = margin_left; participant_widgets[participants[0]].on_row_changed(margin_top == 0, margin_bottom == 0, margin_left == 0, margin_right == 0); return; } ArrayList first_part = new ArrayList(); ArrayList last_part = new ArrayList(); for (int i = 0; i < participants.size; i++) { if (i < Math.ceil((double)participants.size / (double)2)) { first_part.add(participants[i]); } else { last_part.add(participants[i]); } } if (width > height) { reposition_participant_widgets_rec(first_part, width / 2, height, margin_top, margin_right + width / 2, margin_bottom, margin_left); reposition_participant_widgets_rec(last_part, width / 2, height, margin_top, margin_right, margin_bottom, margin_left + width / 2); } else { reposition_participant_widgets_rec(first_part, width, height / 2, margin_top, margin_right, margin_bottom + height / 2, margin_left); reposition_participant_widgets_rec(last_part, width, height / 2, margin_top + height / 2, margin_right, margin_bottom, margin_left); } } public void set_own_video(Widget? widget_) { unset_own_video(); own_video = widget_; if (own_video == null) { own_video = new Box(Orientation.HORIZONTAL, 0); } own_video.hexpand = own_video.vexpand = true; own_video.visible = true; own_video_box.append(own_video); } public void set_own_video_ratio(int width, int height) { if (width / height > 150 / 100) { this.own_video_width = 150; this.own_video_height = height * 150 / width; } else { this.own_video_width = width * 100 / height; this.own_video_height = 100; } } public void unset_own_video() { Widget to_remove = own_video_box.get_first_child(); while (to_remove != null) { own_video_box.remove(to_remove); to_remove = own_video_box.get_first_child(); } } public void set_status(string participant_id, string state) { participant_widgets[participant_id].set_status(state); } public void show_counterpart_ended(string who_terminated, string? reason_name, string? reason_text) { hide_control_elements = false; reveal_control_elements(); string text = ""; if (reason_name == Xmpp.Xep.Jingle.ReasonElement.SUCCESS) { text = _("%s ended the call").printf(who_terminated); } else if (reason_name == Xmpp.Xep.Jingle.ReasonElement.DECLINE || reason_name == Xmpp.Xep.Jingle.ReasonElement.BUSY) { text = _("%s declined the call").printf(who_terminated); } else { if (reason_text == null) { text = "The call has been terminated" + " " + (reason_name ?? ""); } else { text = reason_text + " " + (reason_name ?? ""); } } bottom_bar.show_counterpart_ended(text); } private void reveal_control_elements() { if (!bottom_bar_revealer.child_revealed) { controls_active = true; } timeout_hide_control_elements(); } private void timeout_hide_control_elements() { if (hide_control_handler != 0) { Source.remove(hide_control_handler); hide_control_handler = 0; } if (!hide_control_elements) { return; } hide_control_handler = Timeout.add_seconds(3, () => { if (!hide_control_elements) { return false; } if (bottom_bar.is_menu_active()) { return false; } controls_active = false; hide_control_handler = 0; return false; }); } private bool on_get_child_position(Widget widget, out Gdk.Rectangle allocation) { if (widget == own_video_box) { int width = get_size(Orientation.HORIZONTAL); int height = get_size(Orientation.VERTICAL); allocation = Gdk.Rectangle(); allocation.width = own_video_width; allocation.height = own_video_height; allocation.x = width - own_video_width - 20; allocation.y = height - own_video_height - 20; return true; } return false; } } /* Hack to make the CallHeaderBar feel like a HeaderBar (right click menu, double click, ..) although it isn't set as headerbar. * OutsideHeaderBar is set as a headerbar and it doesn't take any space, but claims to take space (which is actually taken by CallHeaderBar). */ public class OutsideHeaderBar : Gtk.Box { HeaderBar header_bar; public OutsideHeaderBar(HeaderBar header_bar) { this.header_bar = header_bar; // size_allocate.connect_after(on_header_bar_size_allocate); // header_bar.size_allocate.connect(on_header_bar_size_allocate); } public void on_header_bar_size_allocate() { Allocation header_bar_alloc; header_bar.get_allocation(out header_bar_alloc); Allocation alloc; get_allocation(out alloc); alloc.height = header_bar_alloc.height; // set_allocation(alloc); } } }dino-0.4.3/main/src/ui/call_window/call_window_controller.vala0000644000000000000000000004271414452563620023230 0ustar rootrootusing Xmpp; using Gee; using Dino.Entities; using Gtk; public class Dino.Ui.CallWindowController : Object { private CallWindow call_window; private Call call; private CallState call_state; private StreamInteractor stream_interactor; private Calls calls; private Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; private Plugins.VideoCallWidget? own_video = null; private HashMap participant_videos = new HashMap(); private HashMap participant_widgets = new HashMap(); private HashMap peer_states = new HashMap(); private HashMap invite_handler_ids = new HashMap(); private int window_height = -1; private int window_width = -1; private bool window_size_changed = false; private ulong[] call_window_handler_ids = new ulong[0]; private ulong[] bottom_bar_handler_ids = new ulong[0]; private uint inhibit_cookie; public CallWindowController(CallWindow call_window, CallState call_state, StreamInteractor stream_interactor) { this.call_window = call_window; this.call = call_state.call; this.call_state = call_state; this.stream_interactor = stream_interactor; this.calls = stream_interactor.get_module(Calls.IDENTITY); this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK4); call_window.set_default_size(704, 528); // 640x480 * 1.1 this.call_window.bottom_bar.video_enabled = call_state.should_we_send_video(); call_state.terminated.connect((who_terminated, reason_name, reason_text) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(who_terminated.bare_jid, call.account, Conversation.Type.CHAT); string display_name = conversation != null ? Util.get_conversation_display_name(stream_interactor, conversation) : who_terminated.bare_jid.to_string(); call_window.show_counterpart_ended(display_name, reason_name, reason_text); Timeout.add_seconds(3, () => { call_window.close(); call_window.destroy(); return false; }); }); call_state.peer_joined.connect((jid, peer_state) => { connect_peer_signals(peer_state); add_new_participant(peer_state.internal_id, peer_state.jid); }); call_state.peer_left.connect((jid, peer_state, reason_name, reason_text) => { remove_participant(peer_state.internal_id); }); foreach (PeerState peer_state in call_state.peers.values) { connect_peer_signals(peer_state); add_new_participant(peer_state.internal_id, peer_state.jid); } // Call window signals bottom_bar_handler_ids += call_window.bottom_bar.hang_up.connect(() => { call_state.end(); call_window.close(); call_window.destroy(); this.dispose(); }); call_window_handler_ids += call_window.close_request.connect(() => { call_state.end(); this.dispose(); return false; }); bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => { call_state.mute_own_audio(!call_window.bottom_bar.audio_enabled); }); bottom_bar_handler_ids += call_window.bottom_bar.notify["video-enabled"].connect(() => { call_state.mute_own_video(!call_window.bottom_bar.video_enabled); update_own_video(); }); call_window_handler_ids += call_window.notify["default-width"].connect((event) => { if (call_window.default_width == -1) return; int current_width = this.call_window.get_allocated_width(); if (window_width != current_width) { debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Width %i->%i", window_width, current_width); window_size_changed = true; } }); call_window_handler_ids += call_window.notify["default-height"].connect((event) => { if (call_window.default_height == -1) return; int current_height = this.call_window.get_allocated_height(); if (window_height != current_height) { debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Height %i->%i", window_height, current_height); window_size_changed = true; } }); call_window_handler_ids += ((Widget)call_window).realize.connect(() => { capture_window_size(); }); calls.conference_info_received.connect((call, conference_info) => { if (!this.call.equals(call)) return; var participants = new ArrayList(); participants.add_all(participant_videos.keys); foreach (string participant in participants) { remove_participant(participant); } foreach (Jid participant in conference_info.users.keys) { add_new_participant(participant.to_string(), participant); } }); own_video.resolution_changed.connect((width, height) => { if (width == 0 || height == 0) return; call_window.set_own_video_ratio((int)width, (int)height); }); call_window.menu_dump_dot.connect(() => { call_plugin.dump_dot(); }); update_own_video(); update_audio_device_choices(); update_video_device_choices(); var app = GLib.Application.get_default() as Application; inhibit_cookie = app.inhibit(call_window, IDLE | SUSPEND, "Ongoing call"); if (inhibit_cookie == 0) { warning("suspend inhibit request failed or unsupported"); } call_window.close_request.connect(() => { if (inhibit_cookie != 0) { app.uninhibit(inhibit_cookie); } return false; }); } private void invite_button_clicked() { Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(call.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); add_chat_dialog.set_transient_for((Window) call_window.get_root()); add_chat_dialog.title = _("Invite to Call"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { call_state.invite_to_call.begin(jid); }); add_chat_dialog.present(); } private void connect_peer_signals(PeerState peer_state) { string peer_id = peer_state.internal_id; Jid peer_jid = peer_state.jid; peer_states[peer_id] = peer_state; peer_state.connection_ready.connect(() => { call_window.set_status(peer_id, ""); if (participant_widgets.size == 1) { // This is the first peer. // If it can do MUJI, show invite button. call_state.can_convert_into_groupcall.begin((_, res) => { bool can_convert = call_state.can_convert_into_groupcall.end(res); participant_widgets[peer_id].may_show_invite_button = can_convert; }); call_plugin.devices_changed.connect((media, incoming) => { if (media == "audio") update_audio_device_choices(); if (media == "video") update_video_device_choices(); }); update_audio_device_choices(); update_video_device_choices(); } else if (participant_widgets.size > 1) { participant_widgets.values.@foreach((widget) => widget.may_show_invite_button = true); } }); peer_state.counterpart_sends_video_updated.connect((mute) => { if (mute) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(peer_jid.bare_jid, call.account, Conversation.Type.CHAT); call_window.set_placeholder(peer_id, conversation, stream_interactor); participant_videos[peer_id].detach(); } else { if (!(participant_videos[peer_id] is Widget)) return; Widget widget = (Widget) participant_videos[peer_id]; call_window.set_video(peer_id, widget); participant_videos[peer_id].display_stream(peer_state.get_video_stream(), peer_jid); } }); peer_state.info_received.connect((session_info) => { if (session_info == Xmpp.Xep.JingleRtp.CallSessionInfo.RINGING) { call_window.set_status(peer_id, "ringing"); } }); peer_state.encryption_updated.connect((audio_encryption, video_encryption, same) => { update_encryption_indicator(participant_widgets[peer_id].encryption_button_controller, audio_encryption, video_encryption, same); }); } private void update_encryption_indicator(CallEncryptionButtonController encryption_button, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same) { string? title = null; string? icon_name = null; bool show_keys = true; Plugins.Registry registry = Dino.Application.get_default().plugin_registry; Plugins.CallEncryptionEntry? encryption_entry = audio_encryption != null ? registry.call_encryption_entries[audio_encryption.encryption_ns] : null; if (encryption_entry != null) { Plugins.CallEncryptionWidget? encryption_widgets = encryption_entry.get_widget(call.account, audio_encryption); if (encryption_widgets != null) { title = encryption_widgets.get_title(); icon_name = encryption_widgets.get_icon_name(); show_keys = encryption_widgets.show_keys(); } } encryption_button.set_info(title, show_keys, audio_encryption, same ? null : video_encryption); encryption_button.set_icon(audio_encryption != null, icon_name); } private void add_new_participant(string participant_id, Jid jid) { if (participant_widgets.has_key(participant_id)) { warning("[%s] Attempted to add same participant twice: %s", call.account.bare_jid.to_string(), jid.to_string()); return; } debug("[%s] Call window controller | Add participant: %s", call.account.bare_jid.to_string(), jid.to_string()); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, call.account, Conversation.Type.CHAT); string participant_name = conversation != null ? Util.get_conversation_display_name(stream_interactor, conversation) : jid.bare_jid.to_string(); ParticipantWidget participant_widget = new ParticipantWidget(participant_name); participant_widget.may_show_invite_button = !participant_widgets.is_empty; participant_widget.debug_information_clicked.connect(() => { var conn_details_window = new CallConnectionDetailsWindow() { title=participant_name }; conn_details_window.update_content(peer_states[participant_id].get_info()); uint timeout_handle_id = Timeout.add_seconds(1, () => { conn_details_window.update_content(peer_states[participant_id].get_info()); return true; }); conn_details_window.set_transient_for(call_window); conn_details_window.close_request.connect(() => { Source.remove(timeout_handle_id); return false; }); conn_details_window.present(); this.call_window.close_request.connect(() => { conn_details_window.close(); return false; }); }); invite_handler_ids[participant_id] += participant_widget.invite_button_clicked.connect(() => invite_button_clicked()); participant_widgets[participant_id] = participant_widget; call_window.add_participant(participant_id, participant_widget); participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK4); participant_videos[participant_id].resolution_changed.connect((width, height) => { if (window_size_changed || participant_widgets.size > 1) return; if (width == 0 || height == 0) return; if (width > height) { call_window.default_width = 704; call_window.default_height = (int) (height * 704 / width); } else { call_window.default_width = (int) (width * 704 / height); call_window.default_height = 704; } capture_window_size(); }); participant_widget.set_placeholder(conversation, stream_interactor); if (call.direction == Call.DIRECTION_INCOMING) { call_window.set_status(participant_id, "establishing"); } else { call_window.set_status(participant_id, "requested"); } } private void remove_participant(string participant_id) { if (peer_states.has_key(participant_id)) debug(@"[%s] Call window controller | Remove participant: %s", call.account.bare_jid.to_string(), peer_states[participant_id].jid.to_string()); participant_videos.unset(participant_id); participant_widgets[participant_id].disconnect(invite_handler_ids[participant_id]); participant_widgets.unset(participant_id); invite_handler_ids.unset(participant_id); peer_states.unset(participant_id); call_window.remove_participant(participant_id); } private void capture_window_size() { Allocation allocation; this.call_window.get_allocation(out allocation); this.window_height = this.call_window.get_allocated_height(); this.window_width = this.call_window.get_allocated_width(); } private void update_audio_device_choices() { if (call_plugin.get_devices("audio", true).size == 0 || call_plugin.get_devices("audio", false).size == 0) { call_window.bottom_bar.show_audio_device_error(); } else if (call_plugin.get_devices("audio", true).size == 1 && call_plugin.get_devices("audio", false).size == 1) { call_window.bottom_bar.show_audio_device_choices(false); return; } AudioSettingsPopover? audio_settings_popover = call_window.bottom_bar.show_audio_device_choices(true); update_current_audio_device(audio_settings_popover); audio_settings_popover.microphone_selected.connect((device) => { call_state.set_audio_device(device); update_current_audio_device(audio_settings_popover); }); audio_settings_popover.speaker_selected.connect((device) => { call_state.set_audio_device(device); update_current_audio_device(audio_settings_popover); }); } private void update_current_audio_device(AudioSettingsPopover audio_settings_popover) { audio_settings_popover.current_microphone_device = call_state.get_microphone_device(); audio_settings_popover.current_speaker_device = call_state.get_speaker_device(); } private void update_video_device_choices() { int device_count = call_plugin.get_devices("video", false).size; if (device_count == 0) { call_window.bottom_bar.show_video_device_error(); } else if (device_count == 1 || call_state.get_video_device() == null) { call_window.bottom_bar.show_video_device_choices(false); return; } VideoSettingsPopover? video_settings_popover = call_window.bottom_bar.show_video_device_choices(true); update_current_video_device(video_settings_popover); video_settings_popover.camera_selected.connect((device) => { call_state.set_video_device(device); update_current_video_device(video_settings_popover); own_video.display_device(device); }); } private void update_current_video_device(VideoSettingsPopover video_settings_popover) { video_settings_popover.current_device = call_state.get_video_device(); } private void update_own_video() { if (this.call_window.bottom_bar.video_enabled) { Gee.List devices = call_plugin.get_devices("video", false); if (!(own_video is Widget) || devices.is_empty) { call_window.set_own_video(null); } else { Widget widget = (Widget) own_video; call_window.set_own_video(widget); own_video.display_device(call_state.get_video_device()); } } else { own_video.detach(); call_window.unset_own_video(); } } public override void dispose() { foreach (ulong handler_id in call_window_handler_ids) call_window.disconnect(handler_id); foreach (ulong handler_id in bottom_bar_handler_ids) call_window.bottom_bar.disconnect(handler_id); var participant_ids = new ArrayList(); participant_ids.add_all(participant_widgets.keys); foreach (string participant_id in participant_ids) { remove_participant(participant_id); } call_window_handler_ids = bottom_bar_handler_ids = new ulong[0]; own_video.detach(); base.dispose(); } } dino-0.4.3/main/src/ui/call_window/participant_widget.vala0000644000000000000000000001342114452563620022335 0ustar rootrootusing Pango; using Gee; using Xmpp; using Dino.Entities; using Gtk; namespace Dino.Ui { public class ParticipantWidget : Box { public Overlay overlay = new Overlay(); public Widget main_widget; public HeaderBar header_bar = new HeaderBar() { valign=Align.START }; public Label title_label = new Label(""); public Label subtitle_label = new Label(""); public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true }; public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true }; public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5 }; public CallEncryptionButtonController encryption_button_controller; public MenuButton menu_button = new MenuButton() { icon_name="open-menu-symbolic", has_frame=false }; public Button invite_button = new Button.from_icon_name("dino-account-plus") { has_frame=false }; public bool shows_video = false; public string? participant_name; bool is_highest_row = false; bool is_start_row = false; public bool controls_active { get; set; } public bool may_show_invite_button { get; set; } public signal void debug_information_clicked(); public signal void invite_button_clicked(); class construct { install_action("menu.debuginfo", null, (widget, action_name) => { ((ParticipantWidget) widget).debug_information_clicked(); }); } public ParticipantWidget(string participant_name) { encryption_button_controller = new CallEncryptionButtonController(encryption_button); this.participant_name = participant_name; Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; titles_box.add_css_class("titles"); title_label.attributes = new AttrList(); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); header_bar.set_title_widget(titles_box); title_label.label = participant_name; header_bar.add_css_class("participant-header-bar"); header_bar.pack_start(invite_button); header_bar.pack_start(encryption_button); header_bar.pack_end(menu_button); create_menu(); invite_button.clicked.connect(() => invite_button_clicked()); this.append(overlay); overlay.add_overlay(header_bar); this.notify["controls-active"].connect(reveal_or_hide_controls); this.notify["may-show-invite-button"].connect(reveal_or_hide_controls); } public void on_row_changed(bool is_highest, bool is_lowest, bool is_start, bool is_end) { this.is_highest_row = is_highest; this.is_start_row = is_start; header_bar.show_title_buttons = is_highest_row; if (is_highest_row) { Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); if (gtk_settings != null) { string[] buttons = gtk_settings.gtk_decoration_layout.split(":"); header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : ""); } } reveal_or_hide_controls(); } public void set_video(Widget widget) { shows_video = true; widget.visible = true; set_participant_widget(widget); } public void set_placeholder(Conversation? conversation, StreamInteractor stream_interactor) { shows_video = false; Box box = new Box(Orientation.HORIZONTAL, 0); box.add_css_class("video-placeholder-box"); AvatarImage avatar = new AvatarImage() { allow_gray=false, hexpand=true, vexpand=true, halign=Align.CENTER, valign=Align.CENTER, height=100, width=100 }; if (conversation != null) { avatar.set_conversation(stream_interactor, conversation); } else { avatar.set_text("?", false); } box.append(avatar); set_participant_widget(box); } private void set_participant_widget(Widget widget) { widget.hexpand = widget.vexpand = true; main_widget = widget; overlay.set_child(main_widget); } private void create_menu() { Menu menu_model = new Menu(); menu_model.append(_("Debug information"), "menu.debuginfo"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); menu_button.popover = popover_menu; } public void set_status(string state) { subtitle_label.visible = true; if (state == "requested") { subtitle_label.label = _("Calling…"); } else if (state == "ringing") { subtitle_label.label = _("Ringing…"); } else if (state == "establishing") { subtitle_label.label = _("Connecting…"); } else { subtitle_label.visible = false; } } public bool is_menu_active() { return false; } private void reveal_or_hide_controls() { header_bar.opacity = controls_active ? 1.0 : 0.0; invite_button.visible = may_show_invite_button && is_highest_row && is_start_row; } } }dino-0.4.3/main/src/ui/call_window/video_settings_popover.vala0000644000000000000000000000666014452563620023263 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; public class Dino.Ui.VideoSettingsPopover : Gtk.Popover { public signal void camera_selected(Plugins.MediaDevice device); public Plugins.MediaDevice? current_device { get; set; } private HashMap row_device = new HashMap(); public VideoSettingsPopover() { Box box = new Box(Orientation.VERTICAL, 15); box.append(create_camera_box()); this.set_child(box); } private Widget create_camera_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("video", false); Box camera_box = new Box(Orientation.VERTICAL, 10); camera_box.append(new Label("" + _("Cameras") + "") { use_markup=true, xalign=0, can_focus=true /* grab initial focus*/ }); if (devices.size == 0) { camera_box.append(new Label(_("No camera found."))); } else { ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; list_box.set_header_func(listbox_header_func); Frame frame = new Frame(null); frame.set_child(list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_device == null || current_device.id != device.id) { image.opacity = 0; } this.notify["current-device"].connect(() => { if (current_device == null || current_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); list_box.append(list_box_row); row_device[list_box_row] = device; } list_box.row_activated.connect((row) => { if (!row_device.has_key(row)) return; camera_selected(row_device[row]); list_box.unselect_row(row); }); camera_box.append(frame); } return camera_box; } private void listbox_header_func(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } }dino-0.4.3/main/src/ui/chat_input/0000755000000000000000000000000014452563620015442 5ustar rootrootdino-0.4.3/main/src/ui/chat_input/chat_input_controller.vala0000644000000000000000000002574414452563620022724 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { private const string OPEN_CONVERSATION_DETAILS_URI = "x-dino:open-conversation-details"; public class ChatInputController : Object { public signal void activate_last_message_correction(); public signal void file_picker_selected(); public signal void clipboard_pasted(); public new string? conversation_display_name { get; set; } public string? conversation_topic { get; set; } private Conversation? conversation; private ChatInput.View chat_input; private Label status_description_label; private StreamInteractor stream_interactor; private Plugins.InputFieldStatus input_field_status; private ChatTextViewController chat_text_view_controller; private ContentItem? quoted_content_item = null; public ChatInputController(ChatInput.View chat_input, StreamInteractor stream_interactor) { this.chat_input = chat_input; this.status_description_label = chat_input.chat_input_status; this.stream_interactor = stream_interactor; this.chat_text_view_controller = new ChatTextViewController(chat_input.chat_text_view, stream_interactor); chat_input.init(stream_interactor); reset_input_field_status(); var text_input_key_events = new EventControllerKey() { name = "dino-text-input-controller-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); chat_input.chat_text_view.text_view.add_controller(text_input_key_events); chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted()); chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed); chat_text_view_controller.send_text.connect(send_text); chat_input.encryption_widget.encryption_changed.connect(on_encryption_changed); chat_input.file_button.clicked.connect(() => file_picker_selected()); stream_interactor.get_module(MucManager.IDENTITY).received_occupant_role.connect(update_moderated_input_status); stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect(update_moderated_input_status); status_description_label.activate_link.connect((uri) => { if (uri == OPEN_CONVERSATION_DETAILS_URI){ ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation); contact_details_dialog.set_transient_for((Gtk.Window) chat_input.get_root()); contact_details_dialog.present(); } return true; }); SimpleAction quote_action = new SimpleAction("quote", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); quote_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null || !this.conversation.equals(conversation)) return; int content_item_id = variant.get_child_value(1).get_int32(); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, content_item_id); if (content_item == null) return; quoted_content_item = content_item; var quote_model = new Quote.Model.from_content_item(content_item, conversation, stream_interactor) { can_abort = true }; quote_model.aborted.connect(() => { quoted_content_item = null; chat_input.unset_quoted_message(); }); chat_input.set_quoted_message(Quote.get_widget(quote_model)); }); GLib.Application.get_default().add_action(quote_action); } public void set_conversation(Conversation conversation) { reset_input_field_status(); this.quoted_content_item = null; chat_input.unset_quoted_message(); this.conversation = conversation; chat_input.encryption_widget.set_conversation(conversation); chat_input.initialize_for_conversation(conversation); chat_text_view_controller.initialize_for_conversation(conversation); update_moderated_input_status(conversation.account); } public void set_file_upload_active(bool active) { chat_input.set_file_upload_active(active); } private void on_encryption_changed(Encryption encryption) { reset_input_field_status(); if (encryption == Encryption.NONE) return; Application app = GLib.Application.get_default() as Application; var encryption_entry = app.plugin_registry.encryption_list_entries[encryption]; encryption_entry.encryption_activated(conversation, set_input_field_status); } private void set_input_field_status(Plugins.InputFieldStatus? status) { input_field_status = status; chat_input.set_input_state(status.message_type); status_description_label.use_markup = status.contains_markup; status_description_label.label = status.message; chat_input.file_button.sensitive = status.input_state == Plugins.InputFieldStatus.InputState.NORMAL; } private void reset_input_field_status() { set_input_field_status(new Plugins.InputFieldStatus("", Plugins.InputFieldStatus.MessageType.NONE, Plugins.InputFieldStatus.InputState.NORMAL)); } private void send_text() { // Don't do anything if we're in a NO_SEND state. Don't clear the chat input, don't send. if (input_field_status.input_state == Plugins.InputFieldStatus.InputState.NO_SEND) { chat_input.highlight_state_description(); return; } string text = chat_input.chat_text_view.text_view.buffer.text; ContentItem? quoted_content_item_bak = quoted_content_item; // Reset input state. Has do be done before parsing commands, because those directly return. chat_input.chat_text_view.text_view.buffer.text = ""; chat_input.unset_quoted_message(); quoted_content_item = null; if (text.has_prefix("/")) { string[] token = text.split(" ", 2); switch(token[0]) { case "/me": // Just send as is. break; case "/say": if (token.length == 1) return; text = token[1]; break; case "/kick": stream_interactor.get_module(MucManager.IDENTITY).kick(conversation.account, conversation.counterpart, token[1]); return; case "/affiliate": if (token.length > 1) { string[] user_role = token[1].split(" "); if (user_role.length >= 2) { string nick = string.joinv(" ", user_role[0:user_role.length - 1]).strip(); string role = user_role[user_role.length - 1].strip(); stream_interactor.get_module(MucManager.IDENTITY).change_affiliation(conversation.account, conversation.counterpart, nick, role); } } return; case "/nick": stream_interactor.get_module(MucManager.IDENTITY).change_nick.begin(conversation, token[1]); return; case "/ping": Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); try { stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1])); } catch (Xmpp.InvalidJidError e) { warning("Could not ping invalid Jid: %s", e.message); } return; case "/topic": stream_interactor.get_module(MucManager.IDENTITY).change_subject(conversation.account, conversation.counterpart, token[1]); return; default: if (token[0].has_prefix("//")) { text = text.substring(1); } else { string cmd_name = token[0].substring(1); Dino.Application app = GLib.Application.get_default() as Dino.Application; if (app != null && app.plugin_registry.text_commands.has_key(cmd_name)) { string? new_text = app.plugin_registry.text_commands[cmd_name].handle_command(token[1], conversation); if (new_text == null) return; text = (!)new_text; } } break; } } Message out_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(text, conversation); if (quoted_content_item_bak != null) { stream_interactor.get_module(Replies.IDENTITY).set_message_is_reply_to(out_message, quoted_content_item_bak); } stream_interactor.get_module(MessageProcessor.IDENTITY).send_message(out_message, conversation); } private void on_text_input_changed() { if (chat_input.chat_text_view.text_view.buffer.text != "") { stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_entered(conversation); } else { stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_cleared(conversation); } } private void update_moderated_input_status(Account account, Xmpp.Jid? jid = null) { if (conversation != null && conversation.type_ == Conversation.Type.GROUPCHAT){ Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (own_jid == null) return; if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) { string msg_str = _("This conference does not allow you to send messages.") + " " + _("Request permission") + ""; set_input_field_status(new Plugins.InputFieldStatus(msg_str, Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND, true)); } else { reset_input_field_status(); } } } private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") { activate_last_message_correction(); return true; } else { chat_input.do_focus(); } return false; } } } dino-0.4.3/main/src/ui/chat_input/chat_text_view.vala0000644000000000000000000001017314452563620021326 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class ChatTextViewController : Object { public signal void send_text(); public OccupantsTabCompletor occupants_tab_completor; private ChatTextView widget; public ChatTextViewController(ChatTextView widget, StreamInteractor stream_interactor) { this.widget = widget; occupants_tab_completor = new OccupantsTabCompletor(stream_interactor, widget.text_view); widget.send_text.connect(() => { send_text(); }); } public void initialize_for_conversation(Conversation conversation) { occupants_tab_completor.initialize_for_conversation(conversation); } } public class ChatTextView : Box { public signal void send_text(); public signal void cancel_input(); public ScrolledWindow scrolled_window = new ScrolledWindow() { propagate_natural_height=true, max_content_height=300, hexpand=true }; public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 }; private int vscrollbar_min_height; private uint wait_queue_resize; private SmileyConverter smiley_converter; construct { valign = Align.CENTER; scrolled_window.set_child(text_view); this.append(scrolled_window); var text_input_key_events = new EventControllerKey() { name = "dino-text-input-view-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); text_view.add_controller(text_input_key_events); smiley_converter = new SmileyConverter(text_view); scrolled_window.vadjustment.changed.connect(on_upper_notify); text_view.realize.connect(() => { var minimum_size = Requisition(); scrolled_window.get_preferred_size(out minimum_size, null); vscrollbar_min_height = minimum_size.height; }); } public override void dispose() { base.dispose(); if (wait_queue_resize != 0) { Source.remove(wait_queue_resize); wait_queue_resize = 0; } } private void on_upper_notify() { // hack. otherwise the textview would only show the last row(s) when entering a new row on some systems. scrolled_window.height_request = int.min(scrolled_window.max_content_height, (int) scrolled_window.vadjustment.upper + text_view.margin_top + text_view.margin_bottom); scrolled_window.vadjustment.page_size = double.min(scrolled_window.height_request - (text_view.margin_top + text_view.margin_bottom), scrolled_window.vadjustment.upper); // hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately scrolled_window.get_vscrollbar().visible = (scrolled_window.vadjustment.upper > scrolled_window.max_content_height - 2 * this.vscrollbar_min_height); start_queue_resize_if_needed(); } private void start_queue_resize_if_needed() { if (wait_queue_resize == 0) { wait_queue_resize = Timeout.add(100, queue_resize_if_needed); } } private bool queue_resize_if_needed() { if (scrolled_window.get_height() == scrolled_window.height_request) { wait_queue_resize = 0; return false; } else { queue_resize(); return true; } } private bool on_text_input_key_press(EventControllerKey controller, uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval in new uint[]{ Key.Return, Key.KP_Enter }) { // Allow the text view to process the event. Needed for IME. if (text_view.im_context_filter_keypress(controller.get_current_event())) { return true; } if ((state & ModifierType.SHIFT_MASK) > 0) { text_view.buffer.insert_at_cursor("\n", 1); } else if (text_view.buffer.text.strip() != "") { send_text(); } return true; } if (keyval == Key.Escape) { cancel_input(); } return false; } } } dino-0.4.3/main/src/ui/chat_input/encryption_button.vala0000644000000000000000000001055614452563620022103 0ustar rootrootusing Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class EncryptionButton { public signal void encryption_changed(Encryption encryption); private MenuButton menu_button; private Conversation? conversation; private string? current_icon; private StreamInteractor stream_interactor; private SimpleAction action; ulong conversation_encryption_handler_id = -1; public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) { this.stream_interactor = stream_interactor; this.menu_button = menu_button; // Build menu model including "Unencrypted" and all registered encryption entries Menu menu_model = new Menu(); MenuItem unencrypted_item = new MenuItem(_("Unencrypted"), "enc.encryption"); unencrypted_item.set_action_and_target_value("enc.encryption", new Variant.int32(Encryption.NONE)); menu_model.append_item(unencrypted_item); var encryption_entries = new ArrayList(); Application app = GLib.Application.get_default() as Application; encryption_entries.add_all(app.plugin_registry.encryption_list_entries.values); encryption_entries.sort((a,b) => b.name.collate(a.name)); foreach (var e in encryption_entries) { MenuItem item = new MenuItem(e.name, "enc.encryption"); item.set_action_and_target_value("enc.encryption", new Variant.int32(e.encryption)); menu_model.append_item(item); } // Create action to act on menu selections (stateful => radio buttons) SimpleActionGroup action_group = new SimpleActionGroup(); action = new SimpleAction.stateful("encryption", VariantType.INT32, new Variant.int32(Encryption.NONE)); action.activate.connect((parameter) => { action.set_state(parameter); conversation.encryption = (Encryption) parameter.get_int32(); encryption_changed(conversation.encryption); }); action_group.insert(action); menu_button.insert_action_group("enc", action_group); // Create and set popover menu Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); menu_button.popover = popover_menu; stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => { if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(muc_jid)) { update_visibility(); } }); } private void update_encryption_menu_state() { action.set_state(new Variant.int32(conversation.encryption)); action.change_state(new Variant.int32(conversation.encryption)); } private void set_icon(string icon) { if (icon != current_icon) { menu_button.set_icon_name(icon); current_icon = icon; } } private void update_encryption_menu_icon() { set_icon(conversation.encryption == Encryption.NONE ? "changes-allow-symbolic" : "changes-prevent-symbolic"); } private void update_visibility() { if (conversation.encryption != Encryption.NONE) { menu_button.visible = true; return; } switch (conversation.type_) { case Conversation.Type.CHAT: menu_button.visible = true; break; case Conversation.Type.GROUPCHAT_PM: menu_button.visible = false; break; case Conversation.Type.GROUPCHAT: menu_button.visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); break; } } public void set_conversation(Conversation conversation) { if (conversation_encryption_handler_id != -1 && this.conversation != null) { this.conversation.disconnect(conversation_encryption_handler_id); } this.conversation = conversation; update_encryption_menu_state(); update_encryption_menu_icon(); update_visibility(); encryption_changed(this.conversation.encryption); conversation_encryption_handler_id = conversation.notify["encryption"].connect(() => { update_encryption_menu_state(); update_encryption_menu_icon(); }); } } }dino-0.4.3/main/src/ui/chat_input/occupants_tab_completer.vala0000644000000000000000000001204114452563620023204 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { /** * - With given prefix: Complete from occupant list (sorted lexicographically) * - W/o prefix: Complete from received messages (most recent first) * - At the start (with ",") and in the middle of a text * - Backwards tabbing */ public class OccupantsTabCompletor { private StreamInteractor stream_interactor; private Conversation? conversation; private TextView text_input; private Gee.List completions = new ArrayList(); private bool active = false; private int index = -1; public OccupantsTabCompletor(StreamInteractor stream_interactor, TextView text_input) { this.stream_interactor = stream_interactor; this.text_input = text_input; var text_input_key_events = new EventControllerKey(); text_input_key_events.key_pressed.connect(on_text_input_key_press); text_input.add_controller(text_input_key_events); } public void initialize_for_conversation(Conversation conversation) { this.conversation = conversation; } public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (conversation.type_ == Conversation.Type.GROUPCHAT) { if (keyval == Key.Tab || keyval == Key.ISO_Left_Tab) { string text = text_input.buffer.text; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; string word = text.substring(start_index); if (!active) { if (word == "") { completions = generate_completions_from_messages(); } else { completions = generate_completions_from_occupants(word); } if (completions.size > 0) { active = true; index = -1; } } if (keyval != Key.ISO_Group_Shift && active) { text_input.buffer.text = next_completion(keyval == Key.ISO_Left_Tab); return true; } } else if (keyval != Key.Shift_L && active) { active = false; } } return false; } private string next_completion(bool backwards) { string text = text_input.buffer.text; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; string prev_completion = text.substring(start_index); if (index > -1) { start_index = int.max( text.last_index_of(completions[index]), text.substring(0, text.length - 1).last_index_of("\n") ); prev_completion = text.substring(start_index); } if (backwards) { index = int.max(index, 0) - 1; if (index < 0) index = completions.size - 1; } else { index = (index + 1) % (completions.size); } if (start_index == 0) { return completions[index] + ", "; } else { return text.substring(0, text.length - prev_completion.length) + completions[index] + " "; } } private Gee.List generate_completions_from_messages(string? prefix = null) { Gee.List ret = new ArrayList(); Gee.List content_items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_n_latest(conversation, 10); for (int i = content_items.size - 1; i > 0; i--) { string resourcepart = content_items[i].jid.resourcepart; Jid? own_nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (resourcepart != null && resourcepart != "" && (own_nick == null || resourcepart != own_nick.resourcepart) && !ret.contains(resourcepart)) { if (prefix != null && !resourcepart.to_string().down().has_prefix(prefix.down())) continue; ret.add(resourcepart); } } return ret; } private Gee.List generate_completions_from_occupants(string prefix) { // First suggest nicks that have recently writen something Gee.List ret = generate_completions_from_messages(prefix); // Then, suggest other nicks in alphabetical order Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(conversation.counterpart, conversation.account); Gee.List filtered_occupants = new ArrayList(); if (occupants != null) { foreach (Jid jid in occupants) { string resourcepart = jid.resourcepart.to_string(); if (resourcepart.down().has_prefix(prefix.down()) && !ret.contains(resourcepart)) { filtered_occupants.add(resourcepart); } } } filtered_occupants.sort(); ret.add_all(filtered_occupants); return ret; } } } dino-0.4.3/main/src/ui/chat_input/smiley_converter.vala0000644000000000000000000000537214452563620021707 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { class SmileyConverter { private TextView text_input; private static HashMap smiley_translations = new HashMap(); private Regex whitespace_regex = /\s/; static construct { smiley_translations[":)"] = "🙂"; smiley_translations[":D"] = "😀"; smiley_translations[";)"] = "😉"; smiley_translations["O:)"] = "😇"; smiley_translations["O:-)"] = "😇"; smiley_translations["]:>"] = "😈"; smiley_translations[":o"] = "😮"; smiley_translations[":P"] = "😛"; smiley_translations[";P"] = "😜"; smiley_translations[":("] = "🙁"; smiley_translations[":'("] = "😢"; smiley_translations[":/"] = "😕"; smiley_translations["<3"] = "❤️"; smiley_translations[":*"] = "😘️"; smiley_translations[":-*"] = "😘️"; } public SmileyConverter(TextView text_input) { this.text_input = text_input; var text_input_key_events = new EventControllerKey() { name = "dino-smiley-converter-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); text_input.add_controller(text_input_key_events); } public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval == Key.space || keyval == Key.Return) { check_convert(); } return false; } private void check_convert() { if (!Dino.Application.get_default().settings.convert_utf8_smileys) return; TextIter? start_iter; text_input.buffer.get_start_iter(out start_iter); TextIter? cursor_iter; text_input.buffer.get_iter_at_mark(out cursor_iter, text_input.buffer.get_insert()); if (start_iter == null || cursor_iter == null) return; string text = text_input.buffer.get_text(start_iter, cursor_iter, true); foreach (string smiley in smiley_translations.keys) { if (text.has_suffix(smiley)) { if (text.length == smiley.length || whitespace_regex.match(text[text.length - smiley.length - 1].to_string())) { TextIter? end_iter; text_input.buffer.get_end_iter(out end_iter); if (end_iter == null) continue; TextIter smiley_start_iter = cursor_iter; smiley_start_iter.backward_chars(smiley.length); text_input.buffer.delete(ref smiley_start_iter, ref cursor_iter); text_input.buffer.insert_text(ref cursor_iter, smiley_translations[smiley], smiley_translations[smiley].length); } } } } } } dino-0.4.3/main/src/ui/chat_input/view.vala0000644000000000000000000000776614452563620017301 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ChatInput { [GtkTemplate (ui = "/im/dino/Dino/chat_input.ui")] public class View : Box { public string text { owned get { return chat_text_view.text_view.buffer.text; } set { chat_text_view.text_view.buffer.text = value; } } private StreamInteractor stream_interactor; private Conversation? conversation; private HashMap entry_cache = new HashMap(Conversation.hash_func, Conversation.equals_func); [GtkChild] public unowned Frame frame; [GtkChild] public unowned Box quote_box; [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Button file_button; [GtkChild] public unowned MenuButton emoji_button; [GtkChild] public unowned MenuButton encryption_button; [GtkChild] public unowned Separator file_separator; [GtkChild] public unowned Label chat_input_status; public EncryptionButton encryption_widget; public View init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; encryption_widget = new EncryptionButton(stream_interactor, encryption_button); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length); }); chooser.closed.connect(do_focus); emoji_button.set_popover(chooser); file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file")); Util.force_css(frame, "* { border-radius: 3px; }"); return this; } public void set_file_upload_active(bool active) { file_button.visible = active; file_separator.visible = active; } public void initialize_for_conversation(Conversation conversation) { if (this.conversation != null) entry_cache[this.conversation] = chat_text_view.text_view.buffer.text; this.conversation = conversation; chat_text_view.text_view.buffer.text = ""; if (entry_cache.has_key(conversation)) { chat_text_view.text_view.buffer.text = entry_cache[conversation]; } do_focus(); } public void set_input_state(Plugins.InputFieldStatus.MessageType message_type) { switch (message_type) { case Plugins.InputFieldStatus.MessageType.NONE: this.remove_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.INFO: this.remove_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.WARNING: this.add_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.ERROR: this.remove_css_class("dino-input-warning"); this.add_css_class("dino-input-error"); break; } } public void highlight_state_description() { chat_input_status.add_css_class("input-status-highlight-once"); Timeout.add(500, () => { chat_input_status.remove_css_class("input-status-highlight-once"); return false; }); } public void set_quoted_message(Widget quote_widget) { Widget? quote_box_child = quote_box.get_first_child(); if (quote_box_child != null) quote_box.remove(quote_box_child); quote_box.append(quote_widget); quote_box.visible = true; } public void unset_quoted_message() { Widget? quote_box_child = quote_box.get_first_child(); if (quote_box_child != null) quote_box.remove(quote_box_child); quote_box.visible = false; } public void do_focus() { chat_text_view.text_view.grab_focus(); } } } dino-0.4.3/main/src/ui/contact_details/0000755000000000000000000000000014452563620016444 5ustar rootrootdino-0.4.3/main/src/ui/contact_details/blocking_provider.vala0000644000000000000000000000276214452563620023022 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ContactDetails { public class BlockingProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "blocking"; } } private StreamInteractor stream_interactor; public BlockingProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; if (conversation.type_ != Conversation.Type.CHAT) return; if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) { bool is_blocked = stream_interactor.get_module(BlockingManager.IDENTITY).is_blocked(conversation.account, conversation.counterpart); Switch sw = new Switch() { active=is_blocked, valign=Align.CENTER }; sw.state_set.connect((state) => { if (state) { stream_interactor.get_module(BlockingManager.IDENTITY).block(conversation.account, conversation.counterpart); } else { stream_interactor.get_module(BlockingManager.IDENTITY).unblock(conversation.account, conversation.counterpart); } return false; }); contact_details.add(_("Settings"), _("Block"), _("Communication and status updates in either direction are blocked"), sw); } } } } dino-0.4.3/main/src/ui/contact_details/dialog.vala0000644000000000000000000001553114452563620020555 0ustar rootrootusing Gee; using Gtk; using Markup; using Pango; using Dino.Entities; namespace Dino.Ui.ContactDetails { [GtkTemplate (ui = "/im/dino/Dino/contact_details_dialog.ui")] public class Dialog : Gtk.Dialog { [GtkChild] public unowned AvatarImage avatar; [GtkChild] public unowned Util.EntryLabelHybrid name_hybrid; [GtkChild] public unowned Label name_label; [GtkChild] public unowned Label jid_label; [GtkChild] public unowned Label account_label; [GtkChild] public unowned Box main_box; private StreamInteractor stream_interactor; private Conversation conversation; private Plugins.ContactDetails contact_details = new Plugins.ContactDetails(); private HashMap categories = new HashMap(); private Util.LabelHybridGroup hybrid_group = new Util.LabelHybridGroup(); construct { name_hybrid.label.attributes = new AttrList(); name_hybrid.label.attributes.insert(attr_weight_new(Weight.BOLD)); } public Dialog(StreamInteractor stream_interactor, Conversation conversation) { Object(use_header_bar : Util.use_csd() ? 1 : 0); this.stream_interactor = stream_interactor; this.conversation = conversation; title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details"); if (Util.use_csd()) { // TODO get_header_bar directly returns a HeaderBar in vala > 0.48 Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; var title_label = new Label(title); title_label.attributes = new AttrList(); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); titles_box.append(title_label); var subtitle_label = new Label(Util.get_conversation_display_name(stream_interactor, conversation)); subtitle_label.attributes = new AttrList(); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); get_header_bar().set_title_widget(titles_box); } setup_top(); contact_details.add.connect(add_entry); Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_contact_details_entry(new SettingsProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new BlockingProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new MucConfigFormProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor)); foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) { provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4); } close_request.connect(() => { contact_details.save(); return false; }); } private void setup_top() { if (conversation.type_ == Conversation.Type.CHAT) { name_label.visible = false; jid_label.margin_start = new Button().get_style_context().get_padding().left + 1; name_hybrid.text = Util.get_conversation_display_name(stream_interactor, conversation); close_request.connect(() => { if (name_hybrid.text != Util.get_conversation_display_name(stream_interactor, conversation)) { stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, name_hybrid.text); } return false; }); } else { name_hybrid.visible = false; name_label.label = Util.get_conversation_display_name(stream_interactor, conversation); } jid_label.label = conversation.counterpart.to_string(); account_label.label = "via " + conversation.account.bare_jid.to_string(); avatar.set_conversation(stream_interactor, conversation); } private void add_entry(string category, string label, string? description, Object wo) { if (!(wo is Widget)) return; Widget w = (Widget) wo; add_category(category); ListBoxRow list_row = new ListBoxRow() { activatable=false }; Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3 }; list_row.set_child(row); Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true }; if (description != null && description != "") { Box box = new Box(Orientation.VERTICAL, 0); box.append(label_label); Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true }; desc_label.set_markup("%s".printf(Markup.escape_text(description))); desc_label.add_css_class("dim-label"); box.append(desc_label); row.append(box); } else { row.append(label_label); } Widget widget = w; if (widget.get_type().is_a(typeof(Entry))) { Util.EntryLabelHybrid hybrid = new Util.EntryLabelHybrid.wrap(widget as Entry) { xalign=1 }; hybrid_group.add(hybrid); widget = hybrid; } else if (widget.get_type().is_a(typeof(ComboBoxText))) { Util.ComboBoxTextLabelHybrid hybrid = new Util.ComboBoxTextLabelHybrid.wrap(widget as ComboBoxText) { xalign=1 }; hybrid_group.add(hybrid); widget = hybrid; } widget.margin_bottom = 5; widget.margin_top = 5; row.append(widget); categories[category].append(list_row); int width = get_content_area().get_width(); int pref_height, pref_width; get_content_area().measure(Orientation.VERTICAL, width, null, out pref_height, null, null); default_height = pref_height + 48; } private void add_category(string category) { if (!categories.has_key(category)) { ListBox list_box = new ListBox() { selection_mode=SelectionMode.NONE }; categories[category] = list_box; list_box.set_header_func((row, before_row) => { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } }); Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12 }; Label category_label = new Label("") { xalign=0 }; category_label.set_markup(@"$(Markup.escape_text(category))"); box.append(category_label); Frame frame = new Frame(null); frame.set_child(list_box); box.append(frame); main_box.append(box); } } } } dino-0.4.3/main/src/ui/contact_details/muc_config_form_provider.vala0000644000000000000000000000765614452563620024375 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp.Xep; namespace Dino.Ui.ContactDetails { public class MucConfigFormProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "muc_config_form"; } } private StreamInteractor stream_interactor; public MucConfigFormProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; if (conversation.type_ == Conversation.Type.GROUPCHAT) { Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; stream_interactor.get_module(MucManager.IDENTITY).get_config_form.begin(conversation.account, conversation.counterpart, (_, res) => { DataForms.DataForm? data_form = stream_interactor.get_module(MucManager.IDENTITY).get_config_form.end(res); if (data_form == null) return; for (int i = 0; i < data_form.fields.size; i++) { DataForms.DataForm.Field field = data_form.fields[i]; add_field(field, contact_details); } string config_backup = data_form.stanza_node.to_string(); contact_details.save.connect(() => { // Only send the config form if something was changed if (config_backup != data_form.stanza_node.to_string()) { stream_interactor.get_module(MucManager.IDENTITY).set_config_form.begin(conversation.account, conversation.counterpart, data_form); } }); }); } } public static void add_field(DataForms.DataForm.Field field, Plugins.ContactDetails contact_details) { string label = field.label ?? ""; string? desc = null; if (field.var != null) { switch (field.var) { case "muc#roomconfig_roomname": label = _("Name of the room"); break; case "muc#roomconfig_roomdesc": label = _("Description of the room"); break; case "muc#roomconfig_persistentroom": label = _("Persistent"); desc = _("The room will persist after the last occupant leaves"); break; case "muc#roomconfig_publicroom": label = _("Publicly searchable"); break; case "muc#roomconfig_changesubject": label = _("Occupants may change the subject"); break; case "muc#roomconfig_whois": label = _("Permission to view JIDs"); desc = _("Who is allowed to view the occupants' JIDs?"); break; case "muc#roomconfig_roomsecret": label = _("Password"); desc = _("A password to restrict access to the room"); break; case "muc#roomconfig_moderatedroom": label = _("Moderated"); desc = _("Only occupants with voice may send messages"); break; case "muc#roomconfig_membersonly": label = _("Members only"); desc = _("Only members may enter the room"); break; case "muc#roomconfig_historylength": label = _("Message history"); desc = _("Maximum amount of backlog issued by the room"); break; } } Widget? widget = Util.get_data_form_field_widget(field); if (widget != null) contact_details.add(_("Room Configuration"), label, desc, widget); } } } dino-0.4.3/main/src/ui/contact_details/permissions_provider.vala0000644000000000000000000000232314452563620023576 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ContactDetails { public class PermissionsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "permissions"; } } private StreamInteractor stream_interactor; public PermissionsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (own_jid == null) return; if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){ Button voice_request = new Button.with_label(_("Request")); voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart)); contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request); } } } } dino-0.4.3/main/src/ui/contact_details/settings_provider.vala0000644000000000000000000001442414452563620023070 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ContactDetails { public class SettingsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "chat_settings"; } } private StreamInteractor stream_interactor; private string DETAILS_HEADLINE_CHAT = _("Settings"); private string DETAILS_HEADLINE_ROOM = _("Local Settings"); public SettingsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; if (!stream_interactor.get().is_public_room(conversation.account, conversation.counterpart)) { string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT; ComboBoxText combobox_typing = get_combobox(Dino.Application.get_default().settings.send_typing); combobox_typing.active_id = get_setting_id(conversation.send_typing); combobox_typing.changed.connect(() => { conversation.send_typing = get_setting(combobox_typing.active_id); } ); contact_details.add(details_headline, _("Send typing notifications"), "", combobox_typing); } if (conversation.type_ == Conversation.Type.CHAT) { ComboBoxText combobox_marker = get_combobox(Dino.Application.get_default().settings.send_marker); contact_details.add(DETAILS_HEADLINE_CHAT, _("Send read receipts"), "", combobox_marker); combobox_marker.active_id = get_setting_id(conversation.send_marker); combobox_marker.changed.connect(() => { conversation.send_marker = get_setting(combobox_marker.active_id); } ); ComboBoxText combobox_notifications = get_combobox(Dino.Application.get_default().settings.notifications); contact_details.add(DETAILS_HEADLINE_CHAT, _("Notifications"), "", combobox_notifications); combobox_notifications.active_id = get_notify_setting_id(conversation.notify_setting); combobox_notifications.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox_notifications.active_id); } ); } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { ComboBoxText combobox = new ComboBoxText(); combobox.append("default", get_notify_setting_string(Conversation.NotifySetting.DEFAULT, conversation.get_notification_default_setting(stream_interactor))); combobox.append("highlight", get_notify_setting_string(Conversation.NotifySetting.HIGHLIGHT)); combobox.append("on", get_notify_setting_string(Conversation.NotifySetting.ON)); combobox.append("off", get_notify_setting_string(Conversation.NotifySetting.OFF)); contact_details.add(DETAILS_HEADLINE_ROOM, _("Notifications"), "", combobox); combobox.active_id = get_notify_setting_id(conversation.notify_setting); combobox.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox.active_id); } ); } Switch pinned_switch = new Switch() { valign=Align.CENTER }; string category = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT; contact_details.add(category, _("Pin conversation"), _("Pins the conversation to the top of the conversation list"), pinned_switch); pinned_switch.state = conversation.pinned != 0; pinned_switch.state_set.connect((state) => { conversation.pinned = state ? 1 : 0; return false; }); } private Conversation.Setting get_setting(string id) { switch (id) { case "default": return Conversation.Setting.DEFAULT; case "on": return Conversation.Setting.ON; case "off": return Conversation.Setting.OFF; } assert_not_reached(); } private Conversation.NotifySetting get_notify_setting(string id) { switch (id) { case "default": return Conversation.NotifySetting.DEFAULT; case "on": return Conversation.NotifySetting.ON; case "off": return Conversation.NotifySetting.OFF; case "highlight": return Conversation.NotifySetting.HIGHLIGHT; } assert_not_reached(); } private string get_notify_setting_string(Conversation.NotifySetting setting, Conversation.NotifySetting? default_setting = null) { switch (setting) { case Conversation.NotifySetting.ON: return _("On"); case Conversation.NotifySetting.OFF: return _("Off"); case Conversation.NotifySetting.HIGHLIGHT: return _("Only when mentioned"); case Conversation.NotifySetting.DEFAULT: return _("Default: %s").printf(get_notify_setting_string(default_setting)); } assert_not_reached(); } private string get_setting_id(Conversation.Setting setting) { switch (setting) { case Conversation.Setting.DEFAULT: return "default"; case Conversation.Setting.ON: return "on"; case Conversation.Setting.OFF: return "off"; } assert_not_reached(); } private string get_notify_setting_id(Conversation.NotifySetting setting) { switch (setting) { case Conversation.NotifySetting.DEFAULT: return "default"; case Conversation.NotifySetting.ON: return "on"; case Conversation.NotifySetting.OFF: return "off"; case Conversation.NotifySetting.HIGHLIGHT: return "highlight"; } assert_not_reached(); } private ComboBoxText get_combobox(bool default_val) { ComboBoxText combobox = new ComboBoxText(); combobox = new ComboBoxText(); string default_setting = default_val ? _("On") : _("Off"); combobox.append("default", _("Default: %s").printf(default_setting) ); combobox.append("on", _("On")); combobox.append("off", _("Off")); return combobox; } } } dino-0.4.3/main/src/ui/conversation_content_view/0000755000000000000000000000000014452563620020602 5ustar rootrootdino-0.4.3/main/src/ui/conversation_content_view/call_widget.vala0000644000000000000000000002750614452563620023737 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class CallMetaItem : ConversationSummary.ContentMetaItem { private StreamInteractor stream_interactor; public CallMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); this.stream_interactor = stream_interactor; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { CallItem call_item = content_item as CallItem; CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call]; return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation); } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } [GtkTemplate (ui = "/im/dino/Dino/call_widget.ui")] public class CallWidget : SizeRequestBox { [GtkChild] public unowned Image image; [GtkChild] public unowned Label title_label; [GtkChild] public unowned Label subtitle_label; [GtkChild] public unowned Revealer incoming_call_revealer; [GtkChild] public unowned Box outer_additional_box; [GtkChild] public unowned Box incoming_call_box; [GtkChild] public unowned Box multiparty_peer_box; [GtkChild] public unowned Button accept_call_button; [GtkChild] public unowned Button reject_call_button; private StreamInteractor stream_interactor; private CallState call_manager; private Call call; private Conversation conversation; public Call.State call_state { get; set; } // needs to be public for binding private uint time_update_handler_id = 0; private ArrayList multiparty_peer_widgets = new ArrayList(); construct { margin_top = 4; size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; } /** @param call_state Null if it's an old call and we can't interact with it anymore */ public CallWidget(StreamInteractor stream_interactor, Call call, CallState? call_state, Conversation conversation) { this.stream_interactor = stream_interactor; this.call_manager = call_state; this.call = call; this.conversation = conversation; // size_allocate.connect((allocation) => { // if (allocation.height > parent.get_allocated_height()) { // Idle.add(() => { parent.queue_resize(); return false; }); // } // }); call.bind_property("state", this, "call-state"); this.notify["call-state"].connect(update_call_state); if (call_manager != null && (call.state == Call.State.ESTABLISHING || call.state == Call.State.IN_PROGRESS)) { call_manager.peer_joined.connect(update_counterparts); } accept_call_button.clicked.connect(() => { call_manager.accept(); var call_window = new CallWindow(); call_window.controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.present(); }); reject_call_button.clicked.connect(call_manager.reject); update_call_state(); } private void update_counterparts() { if (call.state != Call.State.IN_PROGRESS && call.state != Call.State.ENDED) return; if (call.counterparts.size <= 1 && conversation.type_ == Conversation.Type.CHAT) return; foreach (Widget peer_widget in multiparty_peer_widgets) { multiparty_peer_box.remove(peer_widget); } foreach (Jid counterpart in call.counterparts) { AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2 }; image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid); multiparty_peer_box.append(image); multiparty_peer_widgets.add(image); } AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2 }; image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid); multiparty_peer_box.append(image2); multiparty_peer_widgets.add(image2); outer_additional_box.add_css_class("multiparty-participants"); multiparty_peer_box.visible = true; incoming_call_box.visible = false; incoming_call_revealer.reveal_child = true; } private void update_call_state() { incoming_call_revealer.reveal_child = false; incoming_call_revealer.remove_css_class("incoming"); outer_additional_box.remove_css_class("incoming-call-box"); // It doesn't make sense to display MUC calls as missed or declined by the whole MUC. Just display as ended. // TODO: maybe not let them be missed/declined in first place. Call.State relevant_state = call.state; if (conversation.type_ == Conversation.Type.GROUPCHAT && call.direction == Call.DIRECTION_OUTGOING && (relevant_state == Call.State.MISSED || relevant_state == Call.State.DECLINED)) { relevant_state = Call.State.ENDED; } switch (relevant_state) { case Call.State.RINGING: image.set_from_icon_name("dino-phone-ring-symbolic"); if (call.direction == Call.DIRECTION_INCOMING) { bool video = call_manager.should_we_send_video(); title_label.label = video ? _("Incoming video call") : _("Incoming call"); if (call_manager.invited_to_group_call != null) { title_label.label = video ? _("Incoming video group call") : _("Incoming group call"); } if (stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) { subtitle_label.label = "Ring ring…!"; incoming_call_box.visible = true; incoming_call_revealer.reveal_child = true; incoming_call_revealer.add_css_class("incoming"); outer_additional_box.add_css_class("incoming-call-box"); } else { subtitle_label.label = "Dependencies for call support not met"; } } else { title_label.label = _("Calling…"); subtitle_label.label = "Ring ring…?"; } break; case Call.State.ESTABLISHING: case Call.State.IN_PROGRESS: image.set_from_icon_name("dino-phone-in-talk-symbolic"); title_label.label = _("Call started"); string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time)); subtitle_label.label = _("Started %s ago").printf(duration); time_update_handler_id = Timeout.add_seconds(get_next_time_change() + 1, () => { if (time_update_handler_id != 0) { Source.remove(time_update_handler_id); time_update_handler_id = 0; update_call_state(); } return true; }); break; case Call.State.OTHER_DEVICE: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call"); subtitle_label.label = _("You handled this call on another device"); break; case Call.State.ENDED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call ended"); string formated_end = Util.format_time(call.end_time.to_local(), _("%H∶%M"), _("%l∶%M %p")); string duration = get_duration_string(call.end_time.difference(call.local_time)); subtitle_label.label = _("Ended at %s").printf(formated_end) + " · " + _("Lasted %s").printf(duration); break; case Call.State.MISSED: image.set_from_icon_name("dino-phone-missed-symbolic"); title_label.label = _("Call missed"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You missed this call"); } else { string who = Util.get_conversation_display_name(stream_interactor, conversation); subtitle_label.label = _("%s missed this call").printf(who); } break; case Call.State.DECLINED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call declined"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You declined this call"); } else { string who = Util.get_conversation_display_name(stream_interactor, conversation); subtitle_label.label = _("%s declined this call").printf(who); } break; case Call.State.FAILED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call failed"); subtitle_label.label = "Call failed to establish"; break; } update_counterparts(); } private string get_duration_string(TimeSpan duration) { DateTime a = new DateTime.now_utc(); DateTime b = new DateTime.now_utc(); a.difference(b); TimeSpan remainder_duration = duration; int hours = (int) Math.floor(remainder_duration / TimeSpan.HOUR); remainder_duration -= hours * TimeSpan.HOUR; int minutes = (int) Math.floor(remainder_duration / TimeSpan.MINUTE); remainder_duration -= minutes * TimeSpan.MINUTE; string ret = ""; if (hours > 0) { ret += n("%i hour", "%i hours", hours).printf(hours); } if (minutes > 0) { if (ret.length > 0) { ret += " "; } ret += n("%i minute", "%i minutes", minutes).printf(minutes); } if (ret.length > 0) { return ret; } return _("a few seconds"); } private int get_next_time_change() { DateTime now = new DateTime.now_local(); DateTime item_time = call.local_time; if (now.get_second() < item_time.get_second()) { return item_time.get_second() - now.get_second(); } else { return 60 - (now.get_second() - item_time.get_second()); } } public override void dispose() { base.dispose(); if (time_update_handler_id != 0) { Source.remove(time_update_handler_id); time_update_handler_id = 0; } if (call_manager != null) { call_manager.peer_joined.disconnect(update_counterparts); } } } } dino-0.4.3/main/src/ui/conversation_content_view/chat_state_populator.vala0000644000000000000000000001106114452563620025672 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { class ChatStatePopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "chat_state"; } } private StreamInteractor? stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private MetaChatStateItem? meta_item; public ChatStatePopulator(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).received_state.connect((conversation, state) => { if (current_conversation != null && current_conversation.equals(conversation)) { update_chat_state(); } }); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect((message, conversation) => { if (conversation.equals(current_conversation)) { update_chat_state(); } }); } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; this.meta_item = null; update_chat_state(); } public void close(Conversation conversation) { } public void populate_timespan(Conversation conversation, DateTime from, DateTime to) { } private void update_chat_state() { Gee.List? typing_jids = stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).get_typing_jids(current_conversation); if (meta_item != null && typing_jids == null) { // Remove state (stoped typing) item_collection.remove_item(meta_item); meta_item = null; } else if (meta_item != null && typing_jids != null) { // Update state (other people typing in MUC) meta_item.set_new(typing_jids); } else if (typing_jids != null) { // New state (started typing) meta_item = new MetaChatStateItem(stream_interactor, current_conversation, typing_jids); item_collection.insert_item(meta_item); } } } private class MetaChatStateItem : Plugins.MetaConversationItem { public override DateTime time { get; set; default=new DateTime.now_utc().add_years(10); } private StreamInteractor stream_interactor; private Conversation conversation; private Gee.List jids = new ArrayList(); private Label label; private AvatarImage image; public MetaChatStateItem(StreamInteractor stream_interactor, Conversation conversation, Gee.List jids) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.jids = jids; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { label = new Label("") { xalign=0, vexpand=true }; label.add_css_class("dim-label"); image = new AvatarImage() { margin_top=2, valign=Align.START }; Box image_content_box = new Box(Orientation.HORIZONTAL, 8); image_content_box.append(image); image_content_box.append(label); update(); return image_content_box; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } public void set_new(Gee.List jids) { this.jids = jids; update(); } private void update() { if (image == null || label == null) return; image.set_conversation_participants(stream_interactor, conversation, jids.to_array()); Gee.List display_names = new ArrayList(); foreach (Jid jid in jids) { display_names.add(Util.get_participant_display_name(stream_interactor, conversation, jid)); } string new_text = ""; if (jids.size > 3) { new_text = _("%s, %s and %i others are typing…").printf(display_names[0], display_names[1], jids.size - 2); } else if (jids.size == 3) { new_text = _("%s, %s and %s are typing…").printf(display_names[0], display_names[1], display_names[2]); } else if (jids.size == 2) { new_text =_("%s and %s are typing…").printf(display_names[0], display_names[1]); } else { new_text = _("%s is typing…").printf(display_names[0]); } label.label = new_text; } } } dino-0.4.3/main/src/ui/conversation_content_view/content_populator.vala0000644000000000000000000000737414452563620025241 0ustar rootrootusing Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class ContentProvider : ContentItemCollection, Object { private StreamInteractor stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; public ContentProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void init(Plugins.ConversationItemCollection item_collection, Conversation conversation, Plugins.WidgetType type) { if (current_conversation != null) { stream_interactor.get_module(ContentItemStore.IDENTITY).uninit(current_conversation, this); } current_conversation = conversation; this.item_collection = item_collection; stream_interactor.get_module(ContentItemStore.IDENTITY).init(conversation, this); } public void insert_item(ContentItem item) { item_collection.insert_item(create_content_meta_item(item)); } public void remove_item(ContentItem item) { } public Gee.List populate_latest(Conversation conversation, int n) { Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_n_latest(conversation, n); Gee.List ret = new ArrayList(); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public Gee.List populate_before(Conversation conversation, ContentItem before_item, int n) { Gee.List ret = new ArrayList(); Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_before(conversation, before_item, n); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public Gee.List populate_after(Conversation conversation, ContentItem after_item, int n) { Gee.List ret = new ArrayList(); Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_after(conversation, after_item, n); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public ContentMetaItem get_content_meta_item(ContentItem content_item) { return create_content_meta_item(content_item); } private ContentMetaItem create_content_meta_item(ContentItem content_item) { if (content_item.type_ == MessageItem.TYPE) { return new MessageMetaItem(content_item, stream_interactor); } else if (content_item.type_ == FileItem.TYPE) { return new FileMetaItem(content_item, stream_interactor); } else if (content_item.type_ == CallItem.TYPE) { return new CallMetaItem(content_item, stream_interactor); } critical("Got unknown content item type %s", content_item.type_); return null; } } public abstract class ContentMetaItem : Plugins.MetaConversationItem { public ContentItem content_item; protected ContentMetaItem(ContentItem content_item) { this.jid = content_item.jid; this.time = content_item.time; this.secondary_sort_indicator = content_item.id; this.encryption = content_item.encryption; this.mark = content_item.mark; content_item.bind_property("mark", this, "mark"); content_item.bind_property("encryption", this, "encryption"); this.can_merge = true; this.requires_avatar = true; this.requires_header = true; this.content_item = content_item; } } } dino-0.4.3/main/src/ui/conversation_content_view/conversation_item_skeleton.vala0000644000000000000000000003011414452563620027102 0ustar rootrootusing Gee; using Gdk; using Gtk; using Markup; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, Object { public Grid main_grid { get; set; } public Label name_label { get; set; } public Label time_label { get; set; } public AvatarImage avatar_image { get; set; } public Image encryption_image { get; set; } public Image received_image { get; set; } private HashMap content_widgets = new HashMap(); private bool show_skeleton_ = false; public bool show_skeleton { get { return show_skeleton_; } set { show_skeleton_ = value && content_meta_item != null && content_meta_item.requires_header && content_meta_item.requires_avatar; } } public StreamInteractor stream_interactor; public Conversation conversation { get; set; } public Plugins.MetaConversationItem item; public bool item_in_edit_mode { get; set; } public Entities.Message.Marked item_mark { get; set; } public ContentMetaItem content_meta_item = null; public Widget? widget = null; private ReactionsController? reactions_controller = null; private uint time_update_timeout = 0; private ulong updated_roster_handler_id = 0; public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.item = item; this.content_meta_item = item as ContentMetaItem; item.bind_property("in-edit-mode", this, "item-in-edit-mode"); this.notify["item-in-edit-mode"].connect(update_edit_mode); Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui"); main_grid = (Grid) builder.get_object("main_grid"); main_grid.add_css_class("message-box"); name_label = (Label) builder.get_object("name_label"); time_label = (Label) builder.get_object("time_label"); avatar_image = (AvatarImage) builder.get_object("avatar_image"); encryption_image = (Image) builder.get_object("encrypted_image"); received_image = (Image) builder.get_object("marked_image"); widget = item.get_widget(this, Plugins.WidgetType.GTK4) as Widget; if (widget != null) { widget.valign = Align.END; set_widget(widget, Plugins.WidgetType.GTK4, 2); } if (item.requires_header) { avatar_image.set_conversation_participant(stream_interactor, conversation, item.jid); } this.notify["show-skeleton"].connect(update_margin); this.notify["show-skeleton"].connect(set_header); ContentMetaItem? content_meta_item = item as ContentMetaItem; if (content_meta_item != null) { reactions_controller = new ReactionsController(conversation, content_meta_item.content_item, stream_interactor); reactions_controller.box_activated.connect(on_reaction_box_activated); reactions_controller.init(); } update_margin(); } private void set_header() { if (!show_skeleton) return; update_name_label(); // name_label.style_updated.connect(update_name_label); updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) { update_name_label(); } }); item.notify["encryption"].connect(update_encryption_icon); update_encryption_icon(); if (item.time != null) { update_time(); } item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE); this.notify["item-mark"].connect_after(update_received_mark); update_received_mark(); } public void set_widget(Object object, Plugins.WidgetType type, int priority) { foreach (var content_widget in content_widgets.values) { content_widget.unparent(); } content_widgets[priority] = (Widget) object; int row_no = 1; for (int i = 0; i < 5; i++) { if (!content_widgets.has_key(i)) continue; main_grid.attach(content_widgets[i], 1, row_no, 4, 1); row_no++; } } private void update_margin() { avatar_image.visible = show_skeleton; name_label.visible = show_skeleton; time_label.visible = show_skeleton; encryption_image.visible = show_skeleton; received_image.visible = show_skeleton; if (show_skeleton || content_meta_item == null) { main_grid.add_css_class("has-skeleton"); } } private void update_edit_mode() { if (item.in_edit_mode) { main_grid.add_css_class("edit-mode"); } else { main_grid.remove_css_class("edit-mode"); } } private void update_error_mode() { if (item_mark == Message.Marked.ERROR) { main_grid.add_css_class("error"); } else { main_grid.remove_css_class("error"); } } private void update_encryption_icon() { Application app = GLib.Application.get_default() as Application; ContentMetaItem ci = item as ContentMetaItem; if (item.encryption != Encryption.NONE && item.encryption != Encryption.UNKNOWN && ci != null) { string? icon_name = null; var encryption_entry = app.plugin_registry.encryption_list_entries[item.encryption]; if (encryption_entry != null) icon_name = encryption_entry.get_encryption_icon_name(conversation, ci.content_item); encryption_image.icon_name = icon_name ?? "changes-prevent-symbolic"; encryption_image.visible = true; } if (item.encryption == Encryption.NONE) { if (conversation.encryption != Encryption.NONE) { encryption_image.icon_name = "changes-allow-symbolic"; encryption_image.tooltip_text = Util.string_if_tooltips_active(_("Unencrypted")); Util.force_error_color(encryption_image); encryption_image.visible = true; } else if (conversation.encryption == Encryption.NONE) { encryption_image.icon_name = null; encryption_image.visible = false; } } } private void on_reaction_box_activated(Widget widget) { set_widget(widget, Plugins.WidgetType.GTK4, 3); } private void update_time() { time_label.label = get_relative_time(item.time.to_local()).to_string(); time_update_timeout = Timeout.add_seconds((int) get_next_time_change(item.time), () => { if (this.main_grid.parent == null) return false; update_time(); return false; }); } private void update_name_label() { name_label.label = Util.get_participant_display_name(stream_interactor, conversation, item.jid, true); } private void update_received_mark() { switch (content_meta_item.mark) { case Message.Marked.RECEIVED: received_image.icon_name = "dino-tick-symbolic"; received_image.tooltip_text = Util.string_if_tooltips_active(_("Delivered")); break; case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; received_image.tooltip_text = Util.string_if_tooltips_active(_("Read")); break; case Message.Marked.WONTSEND: received_image.icon_name = "dialog-warning-symbolic"; Util.force_error_color(received_image); Util.force_error_color(time_label); string error_text = Util.string_if_tooltips_active(_("Unable to send message")); received_image.tooltip_text = error_text; time_label.tooltip_text = error_text; break; default: received_image.icon_name = null; break; } } public static int get_next_time_change(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan < 10 * TimeSpan.MINUTE) { if (now.get_second() < datetime.get_second()) { return datetime.get_second() - now.get_second(); } else { return 60 - (now.get_second() - datetime.get_second()); } } else { return (23 - now.get_hour()) * 3600 + (59 - now.get_minute()) * 60 + (59 - now.get_second()); } } public static string format_time(DateTime datetime, string format_24h, string format_12h) { string format = Util.is_24h_format() ? format_24h : format_12h; if (!get_charset(null)) { // No UTF-8 support, use simple colon for time instead format = format.replace("∶", ":"); } return datetime.format(format); } public static string get_relative_time(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan > 365 * TimeSpan.DAY) { return format_time(datetime, /* xgettext:no-c-format */ /* Date + time in 24h format (w/o seconds) */ _("%x, %H∶%M"), /* xgettext:no-c-format */ /* Date + time in 12h format (w/o seconds)*/ _("%x, %l∶%M %p")); } else if (timespan > 7 * TimeSpan.DAY) { return format_time(datetime, /* xgettext:no-c-format */ /* Month, day and time in 24h format (w/o seconds) */ _("%b %d, %H∶%M"), /* xgettext:no-c-format */ /* Month, day and time in 12h format (w/o seconds) */ _("%b %d, %l∶%M %p")); } else if (datetime.get_day_of_month() != now.get_day_of_month()) { return format_time(datetime, /* xgettext:no-c-format */ /* Day of week and time in 24h format (w/o seconds) */ _("%a, %H∶%M"), /* xgettext:no-c-format */ /* Day of week and time in 12h format (w/o seconds) */_("%a, %l∶%M %p")); } else if (timespan > 9 * TimeSpan.MINUTE) { return format_time(datetime, /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M"), /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p")); } else if (timespan > TimeSpan.MINUTE) { ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE); /* xgettext:this is the beginning of a sentence. */ return n("%i min ago", "%i mins ago", mins).printf(mins); } else { return _("Just now"); } } public Widget get_widget() { return main_grid; } public override void dispose() { if (time_update_timeout != 0) { Source.remove(time_update_timeout); time_update_timeout = 0; } if (updated_roster_handler_id != 0){ stream_interactor.get_module(RosterManager.IDENTITY).disconnect(updated_roster_handler_id); updated_roster_handler_id = 0; } reactions_controller = null; // Children won't be disposed automatically if (name_label != null) { name_label.unparent(); name_label.dispose(); name_label = null; } if (time_label != null) { time_label.unparent(); time_label.dispose(); time_label = null; } if (avatar_image != null) { avatar_image.unparent(); avatar_image.dispose(); avatar_image = null; } if (encryption_image != null) { encryption_image.unparent(); encryption_image.dispose(); encryption_image = null; } if (received_image != null) { received_image.unparent(); received_image.dispose(); received_image = null; } base.dispose(); } } } dino-0.4.3/main/src/ui/conversation_content_view/conversation_view.vala0000644000000000000000000006132714452563620025224 0ustar rootrootusing Gee; using Gtk; using Gdk; using Pango; using Dino.Entities; namespace Dino.Ui.ConversationSummary { [GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/view.ui")] public class ConversationView : Widget, Plugins.ConversationItemCollection, Plugins.NotificationCollection { private const int MESSAGE_MENU_BOX_OFFSET = -20; public Conversation? conversation { get; private set; } [GtkChild] public unowned ScrolledWindow scrolled; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Box message_menu_box; [GtkChild] private unowned Box notifications; [GtkChild] private unowned Box main; [GtkChild] private unowned Box main_wrap_box; private HashMap action_buttons = new HashMap(); private Gee.List? message_actions = null; private StreamInteractor stream_interactor; private Gee.TreeSet content_items = new Gee.TreeSet(compare_content_meta_items); private Gee.TreeSet meta_items = new TreeSet(compare_meta_items); private Gee.HashMap item_item_skeletons = new Gee.HashMap(); private Gee.HashMap widgets = new Gee.HashMap(); private Gee.List widget_order = new Gee.ArrayList(); private ContentProvider content_populator; private SubscriptionNotitication subscription_notification; private double? was_value; private double? was_upper; private double? was_page_size; private Mutex reloading_mutex = Mutex(); private bool firstLoad = true; private bool at_current_content = true; private bool reload_messages = true; Widget currently_highlighted = null; ContentMetaItem? current_meta_item = null; double last_y = -1; construct { this.layout_manager = new BinLayout(); // Setup all message menu buttons var correction_button = new Button() { name="correction" }; correction_button.clicked.connect((button) => { on_action_button_clicked(button, null); }); action_buttons["correction"] = correction_button; message_menu_box.append(correction_button); var reply_button = new Button() { name="reply" }; reply_button.clicked.connect((button) => { on_action_button_clicked(button, null); }); action_buttons["reply"] = reply_button; message_menu_box.append(reply_button); var reaction_button = new MenuButton() { name="reaction" }; EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { on_action_button_clicked(reaction_button, new GLib.Variant.string(emoji)); }); reaction_button.popover = chooser; action_buttons["reaction"] = reaction_button; message_menu_box.append(reaction_button); } public ConversationView init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify); scrolled.vadjustment.notify["page-size"].connect(on_upper_notify); scrolled.vadjustment.notify["value"].connect(on_value_notify); content_populator = new ContentProvider(stream_interactor); subscription_notification = new SubscriptionNotitication(stream_interactor); add_meta_notification.connect(on_add_meta_notification); remove_meta_notification.connect(on_remove_meta_notification); Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_conversation_addition_populator(new ChatStatePopulator(stream_interactor)); app.plugin_registry.register_conversation_addition_populator(new DateSeparatorPopulator(stream_interactor)); // Rather than connecting to the leave event of the main_event_box directly, // we connect to the parent event box that also wraps the overlaying message_menu_box. // This eliminates the unwanted leave events emitted on the main_event_box when hovering // the overlaying menu buttons. EventControllerMotion main_wrap_motion_events = new EventControllerMotion(); main_wrap_box.add_controller(main_wrap_motion_events); main_wrap_motion_events.leave.connect(on_leave_notify_event); // The buttons of the overlaying message_menu_box may partially overlap the adjacent // conversation items. We connect to the main_event_box directly to avoid emitting // the pointer motion events as long as the pointer is above the message menu. // This ensures that the currently highlighted item remains unchanged when the pointer // reaches the overlapping part of a button. EventControllerMotion main_motion_events = new EventControllerMotion(); main.add_controller(main_motion_events); main_motion_events.motion.connect(update_highlight); // Process touch events and capture phase to allow highlighting a message without cursor GestureClick click_controller = new GestureClick(); click_controller.touch_only = true; click_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE; main_wrap_box.add_controller(click_controller); click_controller.pressed.connect_after((n, x, y) => { update_highlight(x, y); }); return this; } public void activate_last_message_correction() { Gee.BidirIterator iter = content_items.bidir_iterator(); iter.last(); for (int i = 0; i < 10 && content_items.size > i; i++) { Plugins.MetaConversationItem item = iter.get(); MessageMetaItem message_item = item as MessageMetaItem; if (message_item != null) { if ((conversation.type_ == Conversation.Type.CHAT && message_item.jid.equals_bare(conversation.account.bare_jid)) || (conversation.type_ == Conversation.Type.GROUPCHAT && message_item.jid.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account)))) { message_item.in_edit_mode = true; break; } } iter.previous(); } } private bool is_highlight_fixed() { foreach (Widget widget in action_buttons.values) { MenuButton? menu_button = widget as MenuButton; if (menu_button != null && menu_button.popover.visible) return true; ToggleButton? toggle_button = widget as ToggleButton; if (toggle_button != null && toggle_button.active) return true; } return false; } private void on_leave_notify_event() { if (is_highlight_fixed()) return; if (currently_highlighted != null) { currently_highlighted.remove_css_class("highlight"); currently_highlighted = null; } message_menu_box.visible = false; } private void update_highlight(double x, double y) { if (is_highlight_fixed()) return; if (currently_highlighted != null && (last_y - y).abs() <= 2) { return; } last_y = y; // Get widget under pointer int h = 0; Widget? w = null; foreach (Plugins.MetaConversationItem item in meta_items) { Widget widget = widgets[item]; h += widget.get_allocated_height() + widget.margin_top + widget.margin_bottom; if (h >= y) { w = widget; break; } }; if (currently_highlighted != null) currently_highlighted.remove_css_class("highlight"); currently_highlighted = null; current_meta_item = null; if (w == null) { update_message_menu(); return; } // Get widget coordinates in main double widget_x, widget_y; w.translate_coordinates(main, 0, 0, out widget_x, out widget_y); // Get MessageItem foreach (Plugins.MetaConversationItem item in item_item_skeletons.keys) { if (item_item_skeletons[item].get_widget() == w) { current_meta_item = item as ContentMetaItem; } } update_message_menu(); if (current_meta_item != null) { // Highlight widget currently_highlighted = w; currently_highlighted.add_css_class("highlight"); // Move message menu message_menu_box.margin_top = (int)(widget_y + MESSAGE_MENU_BOX_OFFSET); } } private void update_message_menu() { if (current_meta_item == null) { message_menu_box.visible = false; return; } var current_message_actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); message_actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); if (message_actions != null) { message_menu_box.visible = true; foreach (Widget widget in action_buttons.values) { widget.visible = false; } // Configure as many buttons as we need with the actions for the current meta item foreach (var message_action in current_message_actions) { Widget button_widget = action_buttons[message_action.name]; button_widget.visible = true; if (message_action.name == "reaction") { MenuButton button = (MenuButton) button_widget; button.sensitive = message_action.sensitive; button.icon_name = message_action.icon_name; button.tooltip_text = Util.string_if_tooltips_active(message_action.tooltip); } else if (message_action.callback != null) { Button button = (Button) button_widget; button.sensitive = message_action.sensitive; button.icon_name = message_action.icon_name; button.tooltip_text = Util.string_if_tooltips_active(message_action.tooltip); } } } else { message_menu_box.visible = false; } } public void initialize_for_conversation(Conversation? conversation) { // Workaround for rendering issues if (firstLoad) { main.visible = false; Idle.add(() => { main.visible=true; return false; }); firstLoad = false; } if (conversation == this.conversation && at_current_content) { // Just make sure we are scrolled down if (scrolled.vadjustment.value != scrolled.vadjustment.upper) { scroll_animation(scrolled.vadjustment.upper).play(); } return; } clear(); initialize_for_conversation_(conversation); display_latest(); at_current_content = true; // Scroll to end scrolled.vadjustment.value = scrolled.vadjustment.upper; } private void scroll_and_highlight_item(Plugins.MetaConversationItem target, uint duration = 500) { Widget widget = null; int h = 0; foreach (Plugins.MetaConversationItem item in meta_items) { widget = widgets[item]; if (target == item) { break; } h += widget.get_allocated_height(); } if (widget != widgets[target]) { warning("Target item widget not reached"); return; } double target_height = h - scrolled.vadjustment.page_size * 1/3; Adw.Animation animation = scroll_animation(target_height); animation.done.connect(() => { widget.remove_css_class("highlight-once"); widget.add_css_class("highlight-once"); Timeout.add(5000, () => { widget.remove_css_class("highlight-once"); return false; }); }); animation.play(); } private Adw.Animation scroll_animation(double target) { #if ADW_1_2 return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500, new Adw.PropertyAnimationTarget(scrolled.vadjustment, "value") ); #else return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500, new Adw.CallbackAnimationTarget(value => { scrolled.vadjustment.value = value; }) ); #endif } public void initialize_around_message(Conversation conversation, ContentItem content_item) { if (conversation == this.conversation) { ContentMetaItem? matching_item = content_items.first_match(it => it.content_item.id == content_item.id); if (matching_item != null) { scroll_and_highlight_item(matching_item); return; } } clear(); initialize_for_conversation_(conversation); Gee.List before_items = content_populator.populate_before(conversation, content_item, 40); foreach (ContentMetaItem item in before_items) { do_insert_item(item); } ContentMetaItem meta_item = content_populator.get_content_meta_item(content_item); Widget w = insert_new(meta_item); content_items.add(meta_item); meta_items.add(meta_item); Gee.List after_items = content_populator.populate_after(conversation, content_item, 40); foreach (ContentMetaItem item in after_items) { do_insert_item(item); } if (after_items.size == 40) { at_current_content = false; } // Compute where to jump to for centered message, jump, highlight. reload_messages = false; Timeout.add(700, () => { scroll_and_highlight_item(meta_item, 300); reload_messages = true; return false; }); } private void initialize_for_conversation_(Conversation? conversation) { if (this.conversation == conversation) { print("Re-initialized for %s\n", conversation.counterpart.bare_jid.to_string()); } // Deinitialize old conversation Dino.Application app = Dino.Application.get_default(); if (this.conversation != null) { foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.close(conversation); } foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { populator.close(conversation); } } // Clear data structures clear_notifications(); this.conversation = conversation; // Init for new conversation foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.init(conversation, this, Plugins.WidgetType.GTK4); } content_populator.init(this, conversation, Plugins.WidgetType.GTK4); subscription_notification.init(conversation, this); } private void display_latest() { Gee.List items = content_populator.populate_latest(conversation, 40); foreach (ContentMetaItem item in items) { do_insert_item(item); } Application app = GLib.Application.get_default() as Application; foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { populator.init(conversation, this, Plugins.WidgetType.GTK4); } Idle.add(() => { on_value_notify(); return false; }); } public void insert_item(Plugins.MetaConversationItem item) { if (meta_items.size > 0) { bool after_last = meta_items.last().time.compare(item.time) <= 0; bool within_range = meta_items.last().time.compare(item.time) > 0 && meta_items.first().time.compare(item.time) < 0; bool accept = within_range || (at_current_content && after_last); if (!accept) { return; } } do_insert_item(item); } public void do_insert_item(Plugins.MetaConversationItem item) { lock (meta_items) { insert_new(item); if (item is ContentMetaItem) { content_items.add((ContentMetaItem)item); } meta_items.add(item); } inserted_item(item); } private void remove_item(Plugins.MetaConversationItem item) { ConversationItemSkeleton? skeleton = item_item_skeletons[item]; if (skeleton != null) { main.remove(skeleton.get_widget()); widgets.unset(item); widget_order.remove(skeleton.get_widget()); item_item_skeletons.unset(item); if (item is ContentMetaItem) { content_items.remove((ContentMetaItem)item); } meta_items.remove(item); skeleton.dispose(); } removed_item(item); } public void on_add_meta_notification(Plugins.MetaConversationNotification notification) { Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { add_notification(widget); } } public void on_remove_meta_notification(Plugins.MetaConversationNotification notification){ Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { remove_notification(widget); } } public void add_notification(Widget widget) { notifications.append(widget); Timeout.add(20, () => { notification_revealer.transition_duration = 200; notification_revealer.reveal_child = true; return false; }); } public void remove_notification(Widget widget) { notification_revealer.reveal_child = false; notifications.remove(widget); } private Widget insert_new(Plugins.MetaConversationItem item) { Plugins.MetaConversationItem? lower_item = meta_items.lower(item); // Fill datastructure ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item); item_item_skeletons[item] = item_skeleton; int index = lower_item != null ? widget_order.index_of(item_item_skeletons[lower_item].get_widget()) + 1 : 0; widget_order.insert(index, item_skeleton.get_widget()); // Insert widget widgets[item] = item_skeleton.get_widget(); widgets[item].insert_after(main, item_item_skeletons.has_key(lower_item) ? item_item_skeletons[lower_item].get_widget() : null); if (lower_item != null) { if (can_merge(item, lower_item)) { item_skeleton.show_skeleton = false; } else { item_skeleton.show_skeleton = true; } } else { item_skeleton.show_skeleton = true; } Plugins.MetaConversationItem? upper_item = meta_items.higher(item); if (upper_item != null) { if (!can_merge(upper_item, item)) { ConversationItemSkeleton upper_skeleton = item_item_skeletons[upper_item]; upper_skeleton.show_skeleton = true; } } // If an item from the past was added, add everything between that item and the (post-)first present item if (index == 0) { Dino.Application app = Dino.Application.get_default(); if (widget_order.size == 1) { foreach (Plugins.ConversationAdditionPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.populate_timespan(conversation, item.time, new DateTime.now_utc()); } } else { foreach (Plugins.ConversationAdditionPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.populate_timespan(conversation, item.time, meta_items.higher(item).time); } } } return item_skeleton.get_widget(); } private bool can_merge(Plugins.MetaConversationItem upper_item /*more recent, displayed below*/, Plugins.MetaConversationItem lower_item /*less recent, displayed above*/) { return upper_item.time != null && lower_item.time != null && upper_item.time.difference(lower_item.time) < TimeSpan.MINUTE && upper_item.jid != null && lower_item.jid != null && upper_item.jid.equals(lower_item.jid) && upper_item.encryption == lower_item.encryption && (upper_item.mark == Message.Marked.WONTSEND) == (lower_item.mark == Message.Marked.WONTSEND); } private void on_action_button_clicked(Widget widget, GLib.Variant? variant = null) { foreach (var action in message_actions) { if (action.name != widget.name) continue; action.callback(variant); } } private void on_upper_notify() { if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size if (at_current_content) { Idle.add(() => { // If we do this directly without Idle.add, scrolling down doesn't work properly scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down return false; }); } } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) { scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content } was_upper = scrolled.vadjustment.upper; was_page_size = scrolled.vadjustment.page_size; was_value = scrolled.vadjustment.value; reloading_mutex.trylock(); reloading_mutex.unlock(); } private void on_value_notify() { if (scrolled.vadjustment.value < 400) { load_earlier_messages(); } else if (scrolled.vadjustment.upper - (scrolled.vadjustment.value + scrolled.vadjustment.page_size) < 400) { load_later_messages(); } } private void load_earlier_messages() { was_value = scrolled.vadjustment.value; if (!reloading_mutex.trylock()) return; if (content_items.size > 0) { Gee.List items = content_populator.populate_before(conversation, ((ContentMetaItem) content_items.first()).content_item, 20); foreach (ContentMetaItem item in items) { do_insert_item(item); } } else { reloading_mutex.unlock(); } } private void load_later_messages() { if (!reloading_mutex.trylock()) return; if (content_items.size > 0 && !at_current_content) { Gee.List items = content_populator.populate_after(conversation, ((ContentMetaItem) content_items.last()).content_item, 20); if (items.size == 0) { at_current_content = true; } foreach (ContentMetaItem item in items) { do_insert_item(item); } } else { reloading_mutex.unlock(); } } private static int compare_content_meta_items(ContentMetaItem a, ContentMetaItem b) { return compare_meta_items(a, b); } private static int compare_meta_items(Plugins.MetaConversationItem a, Plugins.MetaConversationItem b) { int cmp1 = a.time.compare(b.time); if (cmp1 != 0) return cmp1; return a.secondary_sort_indicator - b.secondary_sort_indicator; } private void clear() { was_upper = null; was_page_size = null; foreach (var item in content_items) { item.dispose(); } content_items.clear(); meta_items.clear(); widget_order.clear(); foreach (var skeleton in item_item_skeletons.values) { skeleton.dispose(); } item_item_skeletons.clear(); foreach (Widget widget in widgets.values) { widget.unparent(); widget.dispose(); } widgets.clear(); } private void clear_notifications() { // notifications.@foreach((widget) => { notifications.remove(widget); }); notification_revealer.transition_duration = 0; notification_revealer.set_reveal_child(false); } } } dino-0.4.3/main/src/ui/conversation_content_view/date_separator_populator.vala0000644000000000000000000000461014452563620026552 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { class DateSeparatorPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "date_separator"; } } private StreamInteractor stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private Gee.TreeSet insert_times; public DateSeparatorPopulator(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; item_collection.inserted_item.connect(on_inserted_item); this.insert_times = new TreeSet((a, b) => { return a.compare(b); }); } public void close(Conversation conversation) { item_collection.inserted_item.disconnect(on_inserted_item); } public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } private void on_inserted_item(Plugins.MetaConversationItem item) { if (!(item is ContentMetaItem)) return; DateTime time = item.time.to_local(); DateTime msg_date = new DateTime.local(time.get_year(), time.get_month(), time.get_day_of_month(), 0, 0, 0); if (!insert_times.contains(msg_date)) { if (insert_times.lower(msg_date) != null) { item_collection.insert_item(new MetaDateItem(msg_date.to_utc())); } else if (insert_times.size > 0) { item_collection.insert_item(new MetaDateItem(insert_times.first().to_utc())); } insert_times.add(msg_date); } } } public class MetaDateItem : Plugins.MetaConversationItem { public override DateTime time { get; set; } public MetaDateItem(DateTime date) { this.time = date; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new DateSeparator() { model = new ViewModel.CompatDateSeparatorModel(time) }; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } } dino-0.4.3/main/src/ui/conversation_content_view/file_default_widget.vala0000644000000000000000000001354614452563620025446 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")] public class FileDefaultWidget : Box { public signal void clicked(); [GtkChild] public unowned Stack image_stack; [GtkChild] public unowned Label name_label; [GtkChild] public unowned Label mime_label; [GtkChild] public unowned Image content_type_image; [GtkChild] public unowned Spinner spinner; [GtkChild] public unowned MenuButton file_menu; private FileTransfer.State state; public FileDefaultWidget() { EventControllerMotion this_motion_events = new EventControllerMotion(); this.add_controller(this_motion_events); this_motion_events.enter.connect(on_pointer_entered_event); this_motion_events.leave.connect(on_pointer_left_event); GestureClick gesture_click_controller = new GestureClick(); gesture_click_controller.set_button(1); // listen for left clicks this.add_controller(gesture_click_controller); gesture_click_controller.pressed.connect((n_press, x, y) => { // Check whether the click was inside the file menu. Otherwise, open the file. double x_button, y_button; this.translate_coordinates(file_menu, x, y, out x_button, out y_button); if (file_menu.contains(x_button, y_button)) return; this.clicked(); }); } public void update_file_info(string? mime_type, FileTransfer.State state, long size) { this.state = state; spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap content_type_image.icon_name = get_file_icon_name(mime_type); string? mime_description = mime_type != null ? ContentType.get_description(mime_type) : null; switch (state) { case FileTransfer.State.COMPLETE: mime_label.label = mime_description; image_stack.set_visible_child_name("content_type_image"); // Create a menu Menu menu_model = new Menu(); menu_model.append(_("Open"), "file.open"); menu_model.append(_("Save as…"), "file.save_as"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.IN_PROGRESS: mime_label.label = _("Downloading %s…").printf(get_size_string(size)); spinner.start(); image_stack.set_visible_child_name("spinner"); // Create a menu Menu menu_model = new Menu(); menu_model.append(_("Cancel"), "file.cancel_download"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.NOT_STARTED: if (mime_description != null) { mime_label.label = _("%s offered: %s").printf(mime_description, get_size_string(size)); } else if (size != -1) { mime_label.label = _("File offered: %s").printf(get_size_string(size)); } else { mime_label.label = _("File offered"); } image_stack.set_visible_child_name("content_type_image"); break; case FileTransfer.State.FAILED: mime_label.use_markup = true; mime_label.label = "" + _("File transfer failed") + ""; image_stack.set_visible_child_name("content_type_image"); break; } } private void on_pointer_entered_event() { this.set_cursor_from_name("pointer"); content_type_image.opacity = 0.7; if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("download_image"); } if (state == FileTransfer.State.COMPLETE || state == FileTransfer.State.IN_PROGRESS) { file_menu.opacity = 1; } } private void on_pointer_left_event() { if (file_menu.popover != null && file_menu.popover.visible) return; this.set_cursor(null); on_pointer_left(); } private void on_pointer_left() { content_type_image.opacity = 0.5; if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("content_type_image"); } file_menu.opacity = 0; } private static string get_file_icon_name(string? mime_type) { if (mime_type == null) return "dino-file-symbolic"; string generic_icon_name = ContentType.get_generic_icon_name(mime_type) ?? ""; switch (generic_icon_name) { case "audio-x-generic": return "dino-file-music-symbolic"; case "image-x-generic": return "dino-file-image-symbolic"; case "text-x-generic": return "dino-file-document-symbolic"; case "text-x-generic-template": return "dino-file-document-symbolic"; case "video-x-generic": return "dino-file-video-symbolic"; case "x-office-document": return "dino-file-document-symbolic"; case "x-office-spreadsheet": return "dino-file-table-symbolic"; default: return "dino-file-symbolic"; } } private static string get_size_string(long size) { if (size < 1024) { return @"$(size) B"; } else if (size < 1000 * 1000) { return @"$(size / 1000) kB"; } else if (size < 1000 * 1000 * 1000) { return @"$(size / 1000 / 1000) MB"; } else { return @"$(size / 1000 / 1000 / 1000) GB"; } } } } dino-0.4.3/main/src/ui/conversation_content_view/file_image_widget.vala0000644000000000000000000000625514452563620025103 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class FileImageWidget : Box { public FileImageWidget() { this.halign = Align.START; this.add_css_class("file-image-widget"); this.set_cursor_from_name("zoom-in"); } public async void load_from_file(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) throws GLib.Error { Gtk.Box image_overlay_toolbar = new Gtk.Box(Orientation.HORIZONTAL, 0) { halign=Gtk.Align.END, valign=Gtk.Align.START, margin_top=10, margin_start=10, margin_end=10, margin_bottom=10, vexpand=false, visible=false }; image_overlay_toolbar.add_css_class("card"); image_overlay_toolbar.add_css_class("toolbar"); image_overlay_toolbar.add_css_class("overlay-toolbar"); image_overlay_toolbar.set_cursor_from_name("default"); FixedRatioPicture image = new FixedRatioPicture() { min_width=100, min_height=100, max_width=MAX_WIDTH, max_height=MAX_HEIGHT, file=file }; GestureClick gesture_click_controller = new GestureClick(); gesture_click_controller.button = 1; // listen for left clicks gesture_click_controller.released.connect((n_press, x, y) => { switch (gesture_click_controller.get_device().source) { case Gdk.InputSource.TOUCHSCREEN: case Gdk.InputSource.PEN: if (n_press == 1) { image_overlay_toolbar.visible = !image_overlay_toolbar.visible; } else if (n_press == 2) { this.activate_action("file.open", null); image_overlay_toolbar.visible = false; } break; default: this.activate_action("file.open", null); image_overlay_toolbar.visible = false; break; } }); image.add_controller(gesture_click_controller); FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE); string? mime_type = file_info.get_content_type(); MenuButton button = new MenuButton(); button.icon_name = "open-menu"; Menu menu_model = new Menu(); menu_model.append(_("Open"), "file.open"); menu_model.append(_("Save as…"), "file.save_as"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; image_overlay_toolbar.append(button); Overlay overlay = new Overlay(); overlay.set_child(image); overlay.add_overlay(image_overlay_toolbar); overlay.set_measure_overlay(image, true); overlay.set_clip_overlay(image_overlay_toolbar, true); EventControllerMotion this_motion_events = new EventControllerMotion(); this.add_controller(this_motion_events); this_motion_events.enter.connect(() => { image_overlay_toolbar.visible = true; }); this_motion_events.leave.connect(() => { if (button.popover != null && button.popover.visible) return; image_overlay_toolbar.visible = false; }); this.append(overlay); } } } dino-0.4.3/main/src/ui/conversation_content_view/file_widget.vala0000644000000000000000000002160514452563620023735 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Dino.Entities; namespace Dino.Ui { public class FileMetaItem : ConversationSummary.ContentMetaItem { private StreamInteractor stream_interactor; private FileItem file_item; private FileTransfer file_transfer; public FileMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); this.stream_interactor = stream_interactor; this.file_item = content_item as FileItem; this.file_transfer = file_item.file_transfer; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { FileWidget widget = new FileWidget(file_transfer); FileWidgetController widget_controller = new FileWidgetController(widget, file_transfer, stream_interactor); return widget; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { if (file_transfer.provider != 0 || file_transfer.info == null) return null; Gee.List actions = new ArrayList(); if (stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(file_item.conversation, content_item) != null) { actions.add(get_reply_action(content_item, file_item.conversation, stream_interactor)); actions.add(get_reaction_action(content_item, file_item.conversation, stream_interactor)); } return actions; } } public class FileWidget : SizeRequestBox { enum State { IMAGE, DEFAULT } private FileTransfer file_transfer; public FileTransfer.State file_transfer_state { get; set; } public string file_transfer_mime_type { get; set; } private State? state = null; private FileDefaultWidgetController default_widget_controller; private Widget? content = null; public signal void open_file(); public signal void save_file_as(); public signal void start_download(); public signal void cancel_download(); class construct { install_action("file.open", null, (widget, action_name) => { ((FileWidget) widget).open_file(); }); install_action("file.save_as", null, (widget, action_name) => { ((FileWidget) widget).save_file_as(); }); install_action("file.download", null, (widget, action_name) => { ((FileWidget) widget).start_download(); }); install_action("file.cancel", null, (widget, action_name) => { ((FileWidget) widget).cancel_download(); }); } construct { margin_top = 4; size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; } public FileWidget(FileTransfer file_transfer) { this.file_transfer = file_transfer; update_widget.begin(); // size_allocate.connect((allocation) => { // if (allocation.height > parent.get_allocated_height()) { // Idle.add(() => { parent.queue_resize(); return false; }); // } // }); file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); this.notify["file-transfer-state"].connect(update_widget); this.notify["file-transfer-mime-type"].connect(update_widget); } private async void update_widget() { if (show_image() && state != State.IMAGE) { var content_bak = content; FileImageWidget file_image_widget = null; try { file_image_widget = new FileImageWidget(); yield file_image_widget.load_from_file(file_transfer.get_file(), file_transfer.file_name); // If the widget changed in the meanwhile, stop if (content != content_bak) return; if (content != null) this.remove(content); content = file_image_widget; state = State.IMAGE; this.append(content); return; } catch (Error e) { } } if (state != State.DEFAULT) { if (content != null) this.remove(content); FileDefaultWidget default_file_widget = new FileDefaultWidget(); default_widget_controller = new FileDefaultWidgetController(default_file_widget); default_widget_controller.set_file_transfer(file_transfer); content = default_file_widget; this.state = State.DEFAULT; this.append(content); } } private bool show_image() { if (file_transfer.mime_type == null) return false; if (file_transfer.state != FileTransfer.State.COMPLETE && !(file_transfer.direction == FileTransfer.DIRECTION_SENT && file_transfer.state == FileTransfer.State.IN_PROGRESS)) { return false; } foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) { foreach (string mime_type in pixbuf_format.get_mime_types()) { if (mime_type == file_transfer.mime_type) { return true; } } } return false; } public override void dispose() { if (default_widget_controller != null) default_widget_controller.dispose(); default_widget_controller = null; if (content != null) { content.unparent(); content.dispose(); content = null; } base.dispose(); } } public class FileWidgetController : Object { private weak Widget widget; private FileTransfer file_transfer; private StreamInteractor? stream_interactor; public FileWidgetController(FileWidget widget, FileTransfer file_transfer, StreamInteractor? stream_interactor = null) { this.widget = widget; this.ref(); this.widget.weak_ref(() => { this.widget = null; this.unref(); }); this.file_transfer = file_transfer; this.stream_interactor = stream_interactor; widget.open_file.connect(open_file); widget.save_file_as.connect(save_file); widget.start_download.connect(start_download); widget.cancel_download.connect(cancel_download); } private void open_file() { try{ AppInfo.launch_default_for_uri(file_transfer.get_file().get_uri(), null); } catch (Error err) { warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message); } } private void save_file() { var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null); save_dialog.set_modal(true); save_dialog.set_current_name(file_transfer.file_name); save_dialog.response.connect(() => { try{ GLib.File.new_for_uri(file_transfer.get_file().get_uri()).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null); } catch (Error err) { warning("Failed copy file %s - %s", file_transfer.get_file().get_uri(), err.message); } }); save_dialog.show(); } private void start_download() { if (stream_interactor != null) { stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer); } } private void cancel_download() { file_transfer.cancellable.cancel(); } } public class FileDefaultWidgetController : Object { private FileDefaultWidget widget; private FileTransfer? file_transfer; public string file_transfer_state { get; set; } public string file_transfer_mime_type { get; set; } private FileTransfer.State state; public FileDefaultWidgetController(FileDefaultWidget widget) { this.widget = widget; widget.clicked.connect(on_clicked); this.notify["file-transfer-state"].connect(update_file_info); this.notify["file-transfer-mime-type"].connect(update_file_info); } public void set_file_transfer(FileTransfer file_transfer) { this.file_transfer = file_transfer; widget.name_label.label = file_transfer.file_name; file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); update_file_info(); } private void update_file_info() { state = file_transfer.state; widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size); } private void on_clicked() { switch (state) { case FileTransfer.State.COMPLETE: widget.activate_action("file.open", null); break; case FileTransfer.State.NOT_STARTED: widget.activate_action("file.download", null); break; default: // Clicking doesn't do anything in FAILED and IN_PROGRESS states break; } } } } dino-0.4.3/main/src/ui/conversation_content_view/item_actions.vala0000644000000000000000000000460614452563620024133 0ustar rootrootusing Dino.Entities; using Gtk; namespace Dino.Ui { public Plugins.MessageAction get_reaction_action(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { Plugins.MessageAction action = new Plugins.MessageAction(); action.name = "reaction"; action.icon_name = "dino-emoticon-add-symbolic"; action.tooltip = _("Add reaction"); action.callback = (variant) => { string emoji = variant.get_string(); stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); }; // Disable the button if reaction aren't possible. bool supports_reactions = stream_interactor.get_module(Reactions.IDENTITY).conversation_supports_reactions(conversation); string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (!supports_reactions) { action.tooltip = _("This conversation does not support reactions."); action.sensitive = false; } else if (message_id == null) { action.tooltip = "This message does not support reactions."; action.sensitive = false; } return action; } public Plugins.MessageAction get_reply_action(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { Plugins.MessageAction action = new Plugins.MessageAction(); action.name = "reply"; action.icon_name = "mail-reply-sender-symbolic"; action.tooltip = _("Reply"); action.callback = () => { GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(conversation.id), new GLib.Variant.int32(content_item.id) })); }; // Disable the button if replies aren't possible. string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (message_id == null) { action.sensitive = false; if (conversation.type_.is_muc_semantic()) { action.tooltip = _("This conversation does not support replies."); } else { action.tooltip = "This message does not support replies."; } } return action; } }dino-0.4.3/main/src/ui/conversation_content_view/message_widget.vala0000644000000000000000000003034714452563620024445 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Xmpp; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class MessageMetaItem : ContentMetaItem { enum AdditionalInfo { NONE, PENDING, DELIVERY_FAILED } private StreamInteractor stream_interactor; private MessageItem message_item; public Message.Marked marked { get; set; } public Plugins.ConversationItemWidgetInterface outer = null; MessageItemEditMode? edit_mode = null; ChatTextViewController? controller = null; AdditionalInfo additional_info = AdditionalInfo.NONE; ulong realize_id = -1; ulong style_updated_id = -1; ulong marked_notify_handler_id = -1; public Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true }; public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); message_item = content_item as MessageItem; this.stream_interactor = stream_interactor; stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction); label.activate_link.connect(on_label_activate_link); Message message = ((MessageItem) content_item).message; if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) { var binding = message.bind_property("marked", this, "marked"); marked_notify_handler_id = this.notify["marked"].connect(() => { // Currently "pending", but not anymore if (additional_info == AdditionalInfo.PENDING && message.marked != Message.Marked.SENDING && message.marked != Message.Marked.UNSENT) { update_label(); } // Currently "error", but not anymore if (additional_info == AdditionalInfo.DELIVERY_FAILED && message.marked != Message.Marked.ERROR) { update_label(); } // Currently not error, but should be if (additional_info != AdditionalInfo.DELIVERY_FAILED && message.marked == Message.Marked.ERROR) { update_label(); } // Nothing bad can happen anymore if (message.marked in Message.MARKED_RECEIVED) { binding.unbind(); this.disconnect(marked_notify_handler_id); } }); } update_label(); } private string generate_markup_text(ContentItem item) { MessageItem message_item = item as MessageItem; Conversation conversation = message_item.conversation; Message message = message_item.message; bool theme_dependent = false; string markup_text = Dino.message_body_without_reply_fallback(message); if (markup_text.length > 10000) { markup_text = markup_text.substring(0, 10000) + " [" + _("Message too long") + "]"; } if (message.body.has_prefix("/me ")) { markup_text = markup_text.substring(4); } if (conversation.type_ == Conversation.Type.GROUPCHAT) { markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } else { markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } if (message.body.has_prefix("/me ")) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, message.from); markup_text = @"$(Markup.escape_text(display_name)) " + markup_text + ""; } int only_emoji_count = Util.get_only_emoji_count(markup_text); if (only_emoji_count != -1) { string size_str = only_emoji_count < 5 ? "xx-large" : "large"; markup_text = @"" + markup_text + ""; } string dim_color = Util.is_dark_theme(this.label) ? "#BDBDBD" : "#707070"; if (message.edit_to != null) { markup_text += @" (%s)".printf(_("edited")); theme_dependent = true; } // Append message status info additional_info = AdditionalInfo.NONE; if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) { // Append "pending..." iff message has not been sent yet if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) { markup_text += @" %s".printf(_("pending…")); theme_dependent = true; additional_info = AdditionalInfo.PENDING; } else { int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000); Timeout.add(10000 - time_diff, () => { update_label(); return false; }); } } else if (message.direction == Message.DIRECTION_SENT && message.marked == Message.Marked.ERROR) { // Append "delivery failed" if there was a server error string error_color = Util.rgba_to_hex(Util.get_label_pango_color(label, "@error_color")); markup_text += " %s".printf(error_color, _("delivery failed")); theme_dependent = true; additional_info = AdditionalInfo.DELIVERY_FAILED; } if (theme_dependent && realize_id == -1) { realize_id = label.realize.connect(update_label); // style_updated_id = label.style_updated.connect(update_label); } else if (!theme_dependent && realize_id != -1) { label.disconnect(realize_id); label.disconnect(style_updated_id); } return markup_text; } public void update_label() { label.label = generate_markup_text(content_item); } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { this.outer = outer; this.notify["in-edit-mode"].connect(on_in_edit_mode_changed); outer.set_widget(label, Plugins.WidgetType.GTK4, 2); if (message_item.message.quoted_item_id > 0) { var quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(message_item.conversation, message_item.message.quoted_item_id); if (quoted_content_item != null) { var quote_model = new Quote.Model.from_content_item(quoted_content_item, message_item.conversation, stream_interactor); quote_model.jump_to.connect(() => { GLib.Application.get_default().activate_action("jump-to-conversation-message", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(quoted_content_item.id) })); }); var quote_widget = Quote.get_widget(quote_model); outer.set_widget(quote_widget, Plugins.WidgetType.GTK4, 1); } } return label; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { if (in_edit_mode) return null; Gee.List actions = new ArrayList(); bool correction_allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); if (correction_allowed) { Plugins.MessageAction action1 = new Plugins.MessageAction(); action1.name = "correction"; action1.icon_name = "document-edit-symbolic"; action1.tooltip = _("Edit message"); action1.callback = () => { this.in_edit_mode = true; }; actions.add(action1); } actions.add(get_reply_action(content_item, message_item.conversation, stream_interactor)); actions.add(get_reaction_action(content_item, message_item.conversation, stream_interactor)); return actions; } private void on_in_edit_mode_changed() { if (in_edit_mode == false) return; bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); if (allowed) { MessageItem message_item = content_item as MessageItem; Message message = message_item.message; edit_mode = new MessageItemEditMode(); controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor); Conversation conversation = message_item.conversation; controller.initialize_for_conversation(conversation); edit_mode.cancelled.connect(() => { in_edit_mode = false; outer.set_widget(label, Plugins.WidgetType.GTK4, 2); }); edit_mode.send.connect(() => { if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) { on_edit_send(edit_mode.chat_text_view.text_view.buffer.text); } else { // edit_cancelled(); } in_edit_mode = false; outer.set_widget(label, Plugins.WidgetType.GTK4, 2); }); edit_mode.chat_text_view.text_view.buffer.text = message.body; outer.set_widget(edit_mode, Plugins.WidgetType.GTK4, 2); edit_mode.chat_text_view.text_view.grab_focus(); } else { this.in_edit_mode = false; } } private void on_edit_send(string text) { stream_interactor.get_module(MessageCorrection.IDENTITY).send_correction(message_item.conversation, message_item.message, text); this.in_edit_mode = false; } private void on_received_correction(ContentItem content_item) { if (this.content_item.id == content_item.id) { this.content_item = content_item; message_item = content_item as MessageItem; update_label(); } } public static bool on_label_activate_link(string uri) { // Always handle xmpp URIs with Dino if (!uri.has_prefix("xmpp:")) return false; File file = File.new_for_uri(uri); Dino.Application.get_default().open(new File[]{file}, ""); return true; } public override void dispose() { stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.disconnect(on_received_correction); this.notify["in-edit-mode"].disconnect(on_in_edit_mode_changed); if (label != null) { label.unparent(); label.dispose(); label = null; } base.dispose(); } } [GtkTemplate (ui = "/im/dino/Dino/message_item_widget_edit_mode.ui")] public class MessageItemEditMode : Box { public signal void cancelled(); public signal void send(); [GtkChild] public unowned MenuButton emoji_button; [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Button cancel_button; [GtkChild] public unowned Button send_button; [GtkChild] public unowned Frame frame; construct { Util.force_css(frame, "* { border-radius: 3px; padding: 0px 7px; }"); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length); }); emoji_button.set_popover(chooser); chat_text_view.text_view.buffer.changed.connect_after(on_text_view_changed); cancel_button.clicked.connect(() => cancelled()); send_button.clicked.connect(() => send()); chat_text_view.cancel_input.connect(() => cancelled()); chat_text_view.send_text.connect(() => send()); } private void on_text_view_changed() { send_button.sensitive = chat_text_view.text_view.buffer.text != ""; } } } dino-0.4.3/main/src/ui/conversation_content_view/quote_widget.vala0000644000000000000000000000700714452563620024153 0ustar rootrootusing Dino.Ui.ConversationSummary; using Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui.Quote { public class Model : Object { public signal void aborted(); public signal void jump_to(); public string display_name { get; set; } public string message { get; set; } public string display_time { get; set; } public DateTime message_time { get; set; } public StreamInteractor stream_interactor { get; set; } public Conversation conversation { get; set; } public Jid author_jid { get; set; } public bool can_abort { get; set; default=false; } private uint display_time_timeout; public Model.from_content_item(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { this.display_name = Util.get_participant_display_name(stream_interactor, conversation, content_item.jid, true); if (content_item.type_ == MessageItem.TYPE) { var message = ((MessageItem) content_item).message; this.message = Dino.message_body_without_reply_fallback(message); } else if (content_item.type_ == FileItem.TYPE) { var file_transfer = ((FileItem) content_item).file_transfer; this.message = _("File") + ": " + file_transfer.file_name; } this.message_time = content_item.time; update_display_time(); this.stream_interactor = stream_interactor; this.conversation = conversation; this.author_jid = content_item.jid; } private void update_display_time() { this.display_time = ConversationItemSkeleton.get_relative_time(message_time.to_local()); display_time_timeout = Timeout.add_seconds((int) ConversationItemSkeleton.get_next_time_change(message_time), () => { if (display_time_timeout != 0) update_display_time(); return false; }); } public override void dispose() { base.dispose(); if (display_time_timeout != 0) { Source.remove(display_time_timeout); display_time_timeout = 0; } } } public Widget get_widget(Model model) { Builder builder = new Builder.from_resource("/im/dino/Dino/quote.ui"); AvatarImage avatar = (AvatarImage) builder.get_object("avatar"); Label author = (Label) builder.get_object("author"); Label time = (Label) builder.get_object("time"); Label message = (Label) builder.get_object("message"); Button abort_button = (Button) builder.get_object("abort-button"); avatar.set_conversation_participant(model.stream_interactor, model.conversation, model.author_jid); model.bind_property("display-name", author, "label", BindingFlags.SYNC_CREATE); model.bind_property("display-time", time, "label", BindingFlags.SYNC_CREATE); model.bind_property("message", message, "label", BindingFlags.SYNC_CREATE); model.bind_property("can-abort", abort_button, "visible", BindingFlags.SYNC_CREATE); abort_button.clicked.connect(() => { model.aborted(); }); Widget outer = builder.get_object("outer") as Widget; GestureClick gesture_click_controller = new GestureClick(); outer.add_controller(gesture_click_controller); gesture_click_controller.pressed.connect(() => { model.jump_to(); }); return outer; } } dino-0.4.3/main/src/ui/conversation_content_view/reactions_widget.vala0000644000000000000000000001570314452563620025007 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { public class ReactionsController : Object { public signal void box_activated(Widget widget); private Conversation conversation; private Account account; private ContentItem content_item; private StreamInteractor stream_interactor; private HashMap> reactions = new HashMap>(); private ReactionsWidget? widget = null; public ReactionsController(Conversation conversation, ContentItem content_item, StreamInteractor stream_interactor) { this.conversation = conversation; this.account = conversation.account; this.content_item = content_item; this.stream_interactor = stream_interactor; } public void init() { Gee.List reactions = stream_interactor.get_module(Reactions.IDENTITY).get_item_reactions(conversation, content_item); foreach (ReactionUsers reaction_users in reactions) { foreach (Jid jid in reaction_users.jids) { reaction_added(reaction_users.reaction, jid); } } stream_interactor.get_module(Reactions.IDENTITY).reaction_added.connect((account, content_item_id, jid, reaction) => { if (this.content_item.id == content_item_id) { reaction_added(reaction, jid); } }); stream_interactor.get_module(Reactions.IDENTITY).reaction_removed.connect((account, content_item_id, jid, reaction) => { if (this.content_item.id == content_item_id) { reaction_removed(reaction, jid); } }); } private void initialize_widget() { widget = new ReactionsWidget(); widget.emoji_picked.connect((emoji) => { stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); }); widget.emoji_clicked.connect((emoji) => { if (account.bare_jid in reactions[emoji]) { stream_interactor.get_module(Reactions.IDENTITY).remove_reaction(conversation, content_item, emoji); } else { stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); } }); box_activated(widget); } public void reaction_added(string reaction, Jid jid) { if (widget == null) { initialize_widget(); } if (!reactions.has_key(reaction)) { reactions[reaction] = new ArrayList(Jid.equals_func); } if (jid.equals_bare(account.bare_jid) && reactions[reaction].contains(jid)) { return; } reactions[reaction].add(jid); if (reactions[reaction].size == 0) return; widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); } public void reaction_removed(string reaction, Jid jid) { if (!reactions.has_key(reaction)) return; reactions[reaction].remove(jid); if (reactions[reaction].size > 0) { widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); } else { widget.remove_reaction(reaction); reactions.unset(reaction); } if (reactions.size == 0) { widget.unparent(); widget = null; } } private Gee.List update_tooltip(string reaction) { var name_list = new ArrayList(); if (reactions[reaction].size > 0) { if (account.bare_jid in reactions[reaction]) { name_list.add(_("You")); } foreach (Jid jid in reactions[reaction]) { if (jid.equals(account.bare_jid)) continue; name_list.add(Util.get_participant_display_name(stream_interactor, conversation, jid)); } } return name_list; } } public class ReactionsWidget : Grid { public signal void emoji_picked(string emoji); public signal void emoji_clicked(string emoji); private HashMap reaction_counts = new HashMap(); private HashMap reaction_buttons = new HashMap(); private MenuButton add_button; public ReactionsWidget() { this.row_spacing = this.column_spacing = 5; this.margin_top = 2; this.add_css_class("reaction-grid"); add_button = new MenuButton() { tooltip_text= _("Add reaction") }; add_button.add_css_class("pill"); Util.menu_button_set_icon_with_size(add_button, "dino-emoticon-add-symbolic", 14); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { emoji_picked(emoji); }); add_button.set_popover(chooser); } public void update_reaction(string reaction, int count, bool own, Gee.List names) { if (!reaction_buttons.has_key(reaction)) { Label reaction_label = new Label("" + reaction + "") { use_markup=true }; Label count_label = new Label("") { use_markup=true }; Button button = new Button(); button.add_css_class("pill"); Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { halign=Align.CENTER }; reaction_box.append(reaction_label); reaction_box.append(count_label); button.set_child(reaction_box); reaction_counts[reaction] = count_label; reaction_buttons[reaction] = button; this.attach(button, (reaction_buttons.size - 1) % 10, (reaction_buttons.size - 1) / 10, 1, 1); if (add_button.get_parent() != null) this.remove(add_button); this.attach(add_button, reaction_buttons.size % 10, reaction_buttons.size / 10, 1, 1); button.clicked.connect(() => { emoji_clicked(reaction); }); } reaction_counts[reaction].label = "" + count.to_string() + ""; if (own) { reaction_buttons[reaction].add_css_class("own-reaction"); } else { reaction_buttons[reaction].remove_css_class("own-reaction"); } // Build tooltip StringBuilder tooltip_builder = new StringBuilder (); for (int i = 0; i < names.size - 1; i++) { tooltip_builder.append(names[i]); if (i < names.size - 2) tooltip_builder.append(", "); } if (names.size > 1) { tooltip_builder.append(" and "); } tooltip_builder.append(names[names.size - 1]); tooltip_builder.append(" reacted with " + reaction); reaction_buttons[reaction].set_tooltip_text(tooltip_builder.str); } public void remove_reaction(string reaction) { reaction_buttons[reaction].unparent(); } } }dino-0.4.3/main/src/ui/conversation_content_view/subscription_notification.vala0000644000000000000000000000433014452563620026741 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class SubscriptionNotitication : Object { private StreamInteractor stream_interactor; private Conversation conversation; private ConversationView conversation_view; public SubscriptionNotitication(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(PresenceManager.IDENTITY).received_subscription_request.connect((jid, account) => { Conversation relevant_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(relevant_conversation); if (conversation != null && account.equals(conversation.account) && jid.equals(conversation.counterpart)) { show_notification(); } }); } public void init(Conversation conversation, ConversationView conversation_view) { this.conversation = conversation; this.conversation_view = conversation_view; if (stream_interactor.get_module(PresenceManager.IDENTITY).exists_subscription_request(conversation.account, conversation.counterpart)) { show_notification(); } } private void show_notification() { Box box = new Box(Orientation.HORIZONTAL, 5); Button accept_button = new Button.with_label(_("Accept")); Button deny_button = new Button.with_label(_("Deny")); GLib.Application app = GLib.Application.get_default(); accept_button.clicked.connect(() => { app.activate_action("accept-subscription", conversation.id); conversation_view.remove_notification(box); }); deny_button.clicked.connect(() => { app.activate_action("deny-subscription", conversation.id); conversation_view.remove_notification(box); }); box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10 }); box.append(accept_button); box.append(deny_button); conversation_view.add_notification(box); } } } dino-0.4.3/main/src/ui/conversation_list_titlebar.vala0000644000000000000000000000260514452563620021607 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_list_titlebar.ui")] public class ConversationListTitlebar : Gtk.Box { [GtkChild] private unowned MenuButton add_button; [GtkChild] private unowned MenuButton menu_button; public ConversationListTitlebar() { create_add_menu(add_button, menu_button); } } public static Adw.HeaderBar get_conversation_list_titlebar_csd() { Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_list_titlebar_csd.ui"); MenuButton add_button = (MenuButton) builder.get_object("add_button"); MenuButton menu_button = (MenuButton) builder.get_object("menu_button"); create_add_menu(add_button, menu_button); return (Adw.HeaderBar) builder.get_object("header_bar"); } private static void create_add_menu(MenuButton add_button, MenuButton menu_button) { add_button.tooltip_text = Util.string_if_tooltips_active(_("Start Conversation")); Builder add_builder = new Builder.from_resource("/im/dino/Dino/menu_add.ui"); MenuModel add_menu_model = add_builder.get_object("menu_add") as MenuModel; add_button.set_menu_model(add_menu_model); Builder menu_builder = new Builder.from_resource("/im/dino/Dino/menu_app.ui"); MenuModel menu_menu_model = menu_builder.get_object("menu_app") as MenuModel; menu_button.set_menu_model(menu_menu_model); } } dino-0.4.3/main/src/ui/conversation_selector/0000755000000000000000000000000014452563620017716 5ustar rootrootdino-0.4.3/main/src/ui/conversation_selector/conversation_selector.vala0000644000000000000000000001361514452563620025203 0ustar rootrootusing Gdk; using Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class ConversationSelector : Widget { public signal void conversation_selected(Conversation conversation); ListBox list_box = new ListBox() { hexpand=true }; private StreamInteractor stream_interactor; private uint? drag_timeout; private HashMap rows = new HashMap(Conversation.hash_func, Conversation.equals_func); public ConversationSelector init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; list_box.set_parent(this); this.layout_manager = new BinLayout(); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_content_item_received); Timeout.add_seconds(60, () => { foreach (ConversationSelectorRow row in rows.values) row.update(); return true; }); foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) { add_conversation(conversation); } return this; } construct { list_box.set_sort_func(sort); realize.connect(() => { ListBoxRow? first_row = list_box.get_row_at_index(0); if (first_row != null) { list_box.select_row(first_row); row_activated(first_row); } }); list_box.row_activated.connect(row_activated); } public void row_activated(ListBoxRow r) { ConversationSelectorRow? row = r as ConversationSelectorRow; if (row != null) { conversation_selected(row.conversation); } } public void on_conversation_selected(Conversation conversation) { if (!rows.has_key(conversation)) { add_conversation(conversation); } list_box.select_row(rows[conversation]); } private void on_content_item_received(ContentItem item, Conversation conversation) { if (rows.has_key(conversation)) { list_box.invalidate_sort(); } } private void add_conversation(Conversation conversation) { ConversationSelectorRow row; if (!rows.has_key(conversation)) { conversation.notify["pinned"].connect(list_box.invalidate_sort); row = new ConversationSelectorRow(stream_interactor, conversation); rows[conversation] = row; list_box.append(row); row.main_revealer.set_reveal_child(true); // Set up drag motion behaviour (select conversation after timeout) DropControllerMotion drop_motion_controller = new DropControllerMotion(); uint drag_timeout = 0; drop_motion_controller.motion.connect((x, y) => { if (drag_timeout != 0) return; drag_timeout = Timeout.add(200, () => { conversation_selected(conversation); drag_timeout = 0; return false; }); }); drop_motion_controller.leave.connect(() => { if (drag_timeout != 0) { Source.remove(drag_timeout); drag_timeout = 0; } }); row.add_controller(drop_motion_controller); } list_box.invalidate_sort(); } private void select_fallback_conversation(Conversation conversation) { if (list_box.get_selected_row() == rows[conversation]) { int index = rows[conversation].get_index(); ListBoxRow? next_select_row = list_box.get_row_at_index(index + 1); if (next_select_row == null) { next_select_row = list_box.get_row_at_index(index - 1); } if (next_select_row != null) { list_box.select_row(next_select_row); row_activated(next_select_row); } } } private async void remove_conversation(Conversation conversation) { select_fallback_conversation(conversation); if (rows.has_key(conversation)) { conversation.notify["pinned"].disconnect(list_box.invalidate_sort); yield rows[conversation].colapse(); list_box.remove(rows[conversation]); rows.unset(conversation); } } public void loop_conversations(bool backwards) { int index = list_box.get_selected_row().get_index(); int new_index = ((index + (backwards ? -1 : 1)) + rows.size) % rows.size; ListBoxRow? next_select_row = list_box.get_row_at_index(new_index); if (next_select_row != null) { list_box.select_row(next_select_row); row_activated(next_select_row); } } private int sort(ListBoxRow row1, ListBoxRow row2) { ConversationSelectorRow cr1 = row1 as ConversationSelectorRow; ConversationSelectorRow cr2 = row2 as ConversationSelectorRow; if (cr1 != null && cr2 != null) { Conversation c1 = cr1.conversation; Conversation c2 = cr2.conversation; int pin_comp = c2.pinned - c1.pinned; if (pin_comp != 0) return pin_comp; if (c1.last_active == null) return -1; if (c2.last_active == null) return 1; int comp = c2.last_active.compare(c1.last_active); if (comp == 0) { return Util.get_conversation_display_name(stream_interactor, c1) .collate(Util.get_conversation_display_name(stream_interactor, c2)); } else { return comp; } } return 0; } } } dino-0.4.3/main/src/ui/conversation_selector/conversation_selector_row.vala0000644000000000000000000004117714452563620026076 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Dino; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_row.ui")] public class ConversationSelectorRow : ListBoxRow { [GtkChild] protected unowned AvatarImage image; [GtkChild] protected unowned Label name_label; [GtkChild] protected unowned Label time_label; [GtkChild] protected unowned Label nick_label; [GtkChild] protected unowned Label message_label; [GtkChild] protected unowned Label unread_count_label; [GtkChild] protected unowned Button x_button; [GtkChild] protected unowned Revealer time_revealer; [GtkChild] protected unowned Revealer xbutton_revealer; [GtkChild] protected unowned Revealer top_row_revealer; [GtkChild] protected unowned Image pinned_image; [GtkChild] public unowned Revealer main_revealer; public Conversation conversation { get; private set; } protected const int AVATAR_SIZE = 40; protected ContentItem? last_content_item; protected int num_unread = 0; protected StreamInteractor stream_interactor; construct { name_label.attributes = new AttrList(); } public ConversationSelectorRow(StreamInteractor stream_interactor, Conversation conversation) { this.conversation = conversation; this.stream_interactor = stream_interactor; switch (conversation.type_) { case Conversation.Type.CHAT: stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (conversation.account.equals(account) && conversation.counterpart.equals(jid)) { update_name_label(); } }); break; case Conversation.Type.GROUPCHAT: stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_name_label(); update_read(true); // bubble color might have changed } }); stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { update_name_label(); } }); break; case Conversation.Type.GROUPCHAT_PM: break; } // Set tooltip switch (conversation.type_) { case Conversation.Type.CHAT: has_tooltip = Util.use_tooltips(); query_tooltip.connect ((x, y, keyboard_tooltip, tooltip) => { tooltip.set_custom(Util.widget_if_tooltips_active(generate_tooltip())); return true; }); break; case Conversation.Type.GROUPCHAT: has_tooltip = Util.use_tooltips(); set_tooltip_text(Util.string_if_tooltips_active(conversation.counterpart.bare_jid.to_string())); break; case Conversation.Type.GROUPCHAT_PM: break; } stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect((item, c) => { if (conversation.equals(c)) { content_item_received(item); } }); stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect((item) => { if (last_content_item != null && last_content_item.id == item.id) { content_item_received(item); } }); last_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); x_button.clicked.connect(() => { stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); }); image.set_conversation(stream_interactor, conversation); conversation.notify["read-up-to-item"].connect(() => update_read()); conversation.notify["pinned"].connect(() => { update_pinned_icon(); }); update_name_label(); update_pinned_icon(); content_item_received(); } public void update() { update_time_label(); } public void content_item_received(ContentItem? ci = null) { last_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation) ?? ci; update_message_label(); update_time_label(); update_read(); } public async void colapse() { main_revealer.set_transition_type(RevealerTransitionType.SLIDE_UP); main_revealer.set_reveal_child(false); // Animations can be diabled (=> child_revealed immediately false). Wait for completion in case they're enabled. if (main_revealer.child_revealed) { main_revealer.notify["child-revealed"].connect(() => { Idle.add(colapse.callback); }); yield; } } protected void update_name_label() { name_label.label = Util.get_conversation_display_name(stream_interactor, conversation); } private void update_pinned_icon() { pinned_image.visible = conversation.pinned != 0; } protected void update_time_label(DateTime? new_time = null) { if (last_content_item != null) { time_label.visible = true; time_label.label = get_relative_time(last_content_item.time.to_local()); } } protected void update_message_label() { if (last_content_item != null) { switch (last_content_item.type_) { case MessageItem.TYPE: MessageItem message_item = last_content_item as MessageItem; Message last_message = message_item.message; string body = Dino.message_body_without_reply_fallback(last_message); bool me_command = body.has_prefix("/me "); /* If we have a /me command, we always show the display * name, and we don't set me_is_me on * get_participant_display_name, since that will return * "Me" (internationalized), whereas /me commands expect to * be in the third person. We also omit the colon in this * case, and strip off the /me prefix itself. */ if (conversation.type_ == Conversation.Type.GROUPCHAT || me_command) { nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, last_message.from, !me_command); } else if (last_message.direction == Message.DIRECTION_SENT) { nick_label.label = _("Me"); } else { nick_label.label = ""; } if (me_command) { /* Don't slice off the space after /me */ body = body.slice("/me".length, body.length); } else if (nick_label.label.length > 0) { /* TODO: Is this valid for RTL languages? */ nick_label.label += ": "; } change_label_attribute(message_label, attr_style_new(Pango.Style.NORMAL)); message_label.label = Util.summarize_whitespaces_to_space(body); break; case FileItem.TYPE: FileItem file_item = last_content_item as FileItem; FileTransfer transfer = file_item.file_transfer; if (conversation.type_ == Conversation.Type.GROUPCHAT) { // TODO properly display nick for oneself nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, file_item.file_transfer.from, true) + ": "; } else { nick_label.label = transfer.direction == Message.DIRECTION_SENT ? _("Me") + ": " : ""; } bool file_is_image = transfer.mime_type != null && transfer.mime_type.has_prefix("image"); change_label_attribute(message_label, attr_style_new(Pango.Style.ITALIC)); if (transfer.direction == Message.DIRECTION_SENT) { message_label.label = (file_is_image ? _("Image sent") : _("File sent") ); } else { message_label.label = (file_is_image ? _("Image received") : _("File received") ); } break; case CallItem.TYPE: CallItem call_item = (CallItem) last_content_item; Call call = call_item.call; nick_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Me") + ": " : ""; change_label_attribute(message_label, attr_style_new(Pango.Style.ITALIC)); message_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Outgoing call") : _("Incoming call"); break; } nick_label.visible = true; message_label.visible = true; } } private static void change_label_attribute(Label label, owned Attribute attribute) { AttrList copy = label.attributes.copy(); copy.change((owned) attribute); label.attributes = copy; } private bool update_read_pending = false; private bool update_read_pending_force = false; protected void update_read(bool force_update = false) { if (force_update) update_read_pending_force = true; if (update_read_pending) return; update_read_pending = true; Idle.add(() => { update_read_pending = false; update_read_pending_force = false; update_read_idle(update_read_pending_force); return Source.REMOVE; }, Priority.LOW); } private void update_read_idle(bool force_update = false) { int current_num_unread = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation); if (num_unread == current_num_unread && !force_update) return; num_unread = current_num_unread; if (num_unread == 0) { unread_count_label.visible = false; change_label_attribute(name_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(time_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(nick_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(message_label, attr_weight_new(Weight.NORMAL)); } else { unread_count_label.label = num_unread.to_string(); unread_count_label.visible = true; if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) { unread_count_label.add_css_class("unread-count-notify"); unread_count_label.remove_css_class("unread-count"); } else { unread_count_label.add_css_class("unread-count"); unread_count_label.remove_css_class("unread-count-notify"); } change_label_attribute(name_label, attr_weight_new(Weight.BOLD)); change_label_attribute(time_label, attr_weight_new(Weight.BOLD)); change_label_attribute(nick_label, attr_weight_new(Weight.BOLD)); change_label_attribute(message_label, attr_weight_new(Weight.BOLD)); } } public override void state_flags_changed(StateFlags flags) { StateFlags curr_flags = get_state_flags(); if ((curr_flags & StateFlags.PRELIGHT) != 0) { time_revealer.set_reveal_child(false); top_row_revealer.set_reveal_child(false); xbutton_revealer.set_reveal_child(true); } else { time_revealer.set_reveal_child(true); top_row_revealer.set_reveal_child(true); xbutton_revealer.set_reveal_child(false); } } private static Regex dino_resource_regex = /^dino\.[a-f0-9]{8}$/; private Widget generate_tooltip() { Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 }; Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0 }; label.attributes = new AttrList(); label.attributes.insert(attr_weight_new(Weight.BOLD)); grid.attach(label, 0, 0, 2, 1); Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account); if (full_jids == null) return grid; for (int i = 0; i < full_jids.size; i++) { Jid full_jid = full_jids[i]; string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); if (show == null) continue; int i_cache = i; stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => { Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res); Image image = new Image() { hexpand=false, valign=Align.CENTER }; if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) { image.set_from_icon_name("dino-device-phone-symbolic"); } else { image.set_from_icon_name("dino-device-desktop-symbolic"); } if (show == Presence.Stanza.SHOW_AWAY) { Util.force_color(image, "#FF9800"); } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) { Util.force_color(image, "#FF5722"); } else { Util.force_color(image, "#4CAF50"); } string? status = null; if (show == Presence.Stanza.SHOW_AWAY) { status = "away"; } else if (show == Presence.Stanza.SHOW_XA) { status = "not available"; } else if (show == Presence.Stanza.SHOW_DND) { status = "do not disturb"; } var sb = new StringBuilder(); if (identity != null && identity.name != null) { sb.append(identity.name); } else if (full_jid.resourcepart != null && dino_resource_regex.match(full_jid.resourcepart)) { sb.append("Dino"); } else if (full_jid.resourcepart != null) { sb.append(full_jid.resourcepart); } else { return; } if (status != null) { sb.append(" (").append(status).append(")"); } Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0 }; grid.attach(image, 0, i_cache + 1, 1, 1); grid.attach(resource, 1, i_cache + 1, 1, 1); }); } return grid; } private static string get_relative_time(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan > 365 * TimeSpan.DAY) { return datetime.get_year().to_string(); } else if (timespan > 7 * TimeSpan.DAY) { // Day and month // xgettext:no-c-format return datetime.format(_("%b %d")); } else if (timespan > 2 * TimeSpan.DAY) { return datetime.format("%a"); } else if (datetime.get_day_of_month() != now.get_day_of_month()) { return _("Yesterday"); } else if (timespan > 9 * TimeSpan.MINUTE) { return datetime.format(Util.is_24h_format() ? /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M") : /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p")); } else if (timespan > 1 * TimeSpan.MINUTE) { ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE); return n("%i min ago", "%i mins ago", mins).printf(mins); } else { return _("Just now"); } } } } dino-0.4.3/main/src/ui/conversation_titlebar/0000755000000000000000000000000014452563620017704 5ustar rootrootdino-0.4.3/main/src/ui/conversation_titlebar/call_entry.vala0000644000000000000000000001031414452563620022704 0ustar rootrootusing Xmpp; using Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class CallTitlebarEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "call"; } } public double order { get { return 4; } } private MenuButton button = new MenuButton() { tooltip_text=_("Start call") }; private StreamInteractor stream_interactor; private Conversation conversation; public CallTitlebarEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; button.set_icon_name("dino-phone-symbolic"); Menu menu_model = new Menu(); menu_model.append(_("Audio call"), "call.audio"); menu_model.append(_("Video call"), "call.video"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; SimpleActionGroup action_group = new SimpleActionGroup(); SimpleAction audio_call_action = new SimpleAction("audio", null); audio_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); action_group.insert(audio_call_action); SimpleAction video_call_action = new SimpleAction("video", null); video_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); action_group.insert(video_call_action); button.insert_action_group("call", action_group); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state,conversation) => { update_button_state(); }); stream_interactor.get_module(Calls.IDENTITY).call_terminated.connect((call) => { update_button_state(); }); stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect((jid, account) => { if (this.conversation == null) return; if (this.conversation.counterpart.equals_bare(jid) && this.conversation.account.equals(account)) { update_visibility.begin(); } }); stream_interactor.connection_manager.connection_state_changed.connect((account, state) => { update_visibility.begin(); }); } private void open_call_window(CallState call_state) { var call_window = new CallWindow(); var call_controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.controller = call_controller; call_window.present(); update_button_state(); } public new void set_conversation(Conversation conversation) { this.conversation = conversation; update_visibility.begin(); update_button_state(); } private void update_button_state() { button.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress(); } private async void update_visibility() { if (conversation == null) { button.visible = false; return; } Conversation conv_bak = conversation; bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_conversation_do_calls(conversation); if (conv_bak != conversation) return; button.visible = can_do_calls; } public new void unset_conversation() { } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.4.3/main/src/ui/conversation_titlebar/conversation_titlebar.vala0000644000000000000000000001123214452563620025150 0ustar rootrootusing Gtk; using Gee; using Pango; using Dino.Entities; namespace Dino.Ui { public interface ConversationTitlebar : Object { public abstract string? subtitle { get; set; } public abstract string? title { get; set; } public abstract void insert_button(Widget button); public abstract Widget get_widget(); public abstract bool back_button_visible{ get; set; } public signal void back_pressed(); } public class ConversationTitlebarNoCsd : ConversationTitlebar, Object { public Box main = new Box(Orientation.HORIZONTAL, 0); public string? title { get { return title_label.label; } set { this.title_label.label = value; } } public string? subtitle { get { return subtitle_label.label; } set { this.subtitle_label.label = "" + value + ""; this.subtitle_label.visible = (value != null); } } public bool back_button_visible { get { return back_revealer.reveal_child; } set { back_revealer.reveal_child = value; } } private Box widgets_box = new Box(Orientation.HORIZONTAL, 7) { margin_start=15, valign=Align.END }; private Label title_label = new Label("") { ellipsize=EllipsizeMode.END }; private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false }; private Revealer back_revealer; construct { Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true }; main.append(content_box); back_revealer = new Revealer() { visible = true, transition_type = RevealerTransitionType.SLIDE_RIGHT, transition_duration = 200, can_focus = false, reveal_child = false }; Button back_button = new Button.from_icon_name("go-previous-symbolic") { visible = true, valign = Align.CENTER, use_underline = true }; back_button.get_style_context().add_class("image-button"); back_button.clicked.connect(() => back_pressed()); back_revealer.set_child(back_button); content_box.append(back_revealer); Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true }; content_box.append(titles_box); titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); content_box.append(widgets_box); } public ConversationTitlebarNoCsd() { main.add_css_class("dino-header-right"); } public void insert_button(Widget button) { widgets_box.prepend(button); } public Widget get_widget() { return main; } } public class ConversationTitlebarCsd : ConversationTitlebar, Object { public new string? title { get { return title_label.label; } set { title_label.label = value; } } public new string? subtitle { get { return subtitle_label.label; } set { subtitle_label.label = value; subtitle_label.visible = (value != null); } } public bool back_button_visible { get { return back_revealer.reveal_child; } set { back_revealer.reveal_child = value; } } public Adw.HeaderBar header_bar = new Adw.HeaderBar(); private Label title_label = new Label("") { ellipsize=EllipsizeMode.END }; private Label subtitle_label = new Label("") { ellipsize=EllipsizeMode.END, visible=false }; private Revealer back_revealer; public ConversationTitlebarCsd() { Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; title_label.attributes = new AttrList(); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); back_revealer = new Revealer() { visible = true, transition_type = RevealerTransitionType.SLIDE_RIGHT, transition_duration = 200, can_focus = false, reveal_child = false }; Button back_button = new Button.from_icon_name("go-previous-symbolic") { visible = true, valign = Align.CENTER, use_underline = true }; back_button.get_style_context().add_class("image-button"); back_button.clicked.connect(() => back_pressed()); back_revealer.set_child(back_button); header_bar.pack_start(back_revealer); header_bar.set_title_widget(titles_box); } public void insert_button(Widget button) { header_bar.pack_end(button); } public Widget get_widget() { return header_bar; } } } dino-0.4.3/main/src/ui/conversation_titlebar/menu_entry.vala0000644000000000000000000000264714452563620022747 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { class MenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "menu"; } } public double order { get { return 0; } } StreamInteractor stream_interactor; private Conversation? conversation; Button button = new Button() { icon_name="open-menu-symbolic" }; public MenuEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; button.clicked.connect(on_clicked); } public new void set_conversation(Conversation conversation) { button.sensitive = true; this.conversation = conversation; if (conversation.type_ == Conversation.Type.GROUPCHAT) { button.tooltip_text = Util.string_if_tooltips_active("Channel details"); } else { button.tooltip_text = Util.string_if_tooltips_active("Conversation details"); } } public new void unset_conversation() { button.sensitive = false; } private void on_clicked() { ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation); contact_details_dialog.set_transient_for((Window) button.get_root()); contact_details_dialog.present(); } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.4.3/main/src/ui/conversation_titlebar/occupants_entry.vala0000644000000000000000000000243714452563620023777 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "occupants"; } } public double order { get { return 3; } } StreamInteractor stream_interactor; private Conversation? conversation; private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=Util.string_if_tooltips_active(_("Members")), visible=false }; private OccupantMenu.View menu = null; public OccupantsEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public new void set_conversation(Conversation conversation) { this.conversation = conversation; if (conversation.type_ == Conversation.Type.GROUPCHAT) { button.visible = true; OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, conversation); button.set_popover(new_menu); menu = new_menu; } else { button.visible = false; } } public new void unset_conversation() { button.visible = false; } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.4.3/main/src/ui/conversation_titlebar/search_entry.vala0000644000000000000000000000131714452563620023241 0ustar rootrootusing Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class SearchMenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "search"; } } public double order { get { return 1; } } public ToggleButton button = new ToggleButton() { tooltip_text=Util.string_if_tooltips_active(_("Search messages")) }; public SearchMenuEntry() { button.set_icon_name("system-search-symbolic"); } public new void set_conversation(Conversation conversation) { } public new void unset_conversation() { } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.4.3/main/src/ui/conversation_view.vala0000644000000000000000000000324214452563620017716 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_view.ui")] public class ConversationView : Widget { // [GtkChild] public unowned ScrolledWindow conversation_scrolled; [GtkChild] public unowned Overlay overlay; [GtkChild] public unowned Revealer goto_end_revealer; [GtkChild] public unowned Button goto_end_button; [GtkChild] public unowned ChatInput.View chat_input; [GtkChild] public unowned ConversationSummary.ConversationView conversation_frame; [GtkChild] public unowned Revealer white_revealer; public ListView list_view = new ListView(null, null); public bool at_current_content = true; construct { this.layout_manager = new BinLayout(); white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed); // conversation_scrolled.set_child(list_view); // list_view.set_factory(get_item_factory()); } public void add_overlay_dialog(Widget widget) { Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100 }; revealer.set_child(widget); overlay.add_overlay(revealer); revealer.reveal_child = true; white_revealer.visible = true; white_revealer.reveal_child = true; widget.destroy.connect(() => { overlay.remove_overlay(revealer); white_revealer.reveal_child = false; chat_input.do_focus(); }); } private void on_child_revealed_changed() { if (!white_revealer.child_revealed) { white_revealer.visible = false; } } } } dino-0.4.3/main/src/ui/conversation_view_controller.vala0000644000000000000000000002773014452563620022171 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class ConversationViewController : Object { public new string? conversation_display_name { get; set; } public string? conversation_topic { get; set; } private Application app; private ConversationView view; private Widget? overlay_dialog; private ConversationTitlebar titlebar; public SearchMenuEntry search_menu_entry = new SearchMenuEntry(); public ListView list_view = new ListView(null, null); private DropTarget drop_event_controller = new DropTarget(typeof(File), DragAction.COPY ); private ChatInputController chat_input_controller; private StreamInteractor stream_interactor; private Conversation? conversation; public ConversationViewController(ConversationView view, ConversationTitlebar titlebar, StreamInteractor stream_interactor) { this.view = view; this.titlebar = titlebar; this.stream_interactor = stream_interactor; this.app = GLib.Application.get_default() as Application; this.chat_input_controller = new ChatInputController(view.chat_input, stream_interactor); chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction); chat_input_controller.file_picker_selected.connect(open_file_picker); chat_input_controller.clipboard_pasted.connect(on_clipboard_paste); view.conversation_frame.init(stream_interactor); // drag 'n drop file upload drop_event_controller.on_drop.connect(this.on_drag_data_received); // forward key presses var key_controller = new EventControllerKey() { name = "dino-forward-to-input-key-events-1" }; key_controller.key_pressed.connect(forward_key_press_to_chat_input); view.conversation_frame.add_controller(key_controller); var key_controller2 = new EventControllerKey() { name = "dino-forward-to-input-key-events-2" }; key_controller2.key_pressed.connect(forward_key_press_to_chat_input); view.chat_input.add_controller(key_controller2); var key_controller3 = new EventControllerKey() { name = "dino-forward-to-input-key-events-3" }; key_controller3.key_pressed.connect(forward_key_press_to_chat_input); titlebar.get_widget().add_controller(key_controller3); // goto-end floating button var vadjustment = view.conversation_frame.scrolled.vadjustment; vadjustment.notify["value"].connect(() => { bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size; view.goto_end_revealer.reveal_child = button_active; view.goto_end_revealer.visible = button_active; }); view.goto_end_button.clicked.connect(() => { view.conversation_frame.initialize_for_conversation(conversation); }); // Update conversation display name & topic this.bind_property("conversation-display-name", titlebar, "title"); this.bind_property("conversation-topic", titlebar, "subtitle"); stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_conversation_display_name(); } }); stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { update_conversation_display_name(); } }); stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_conversation_topic(subject); } }); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(jid)) { update_conversation_display_name(); } }); stream_interactor.get_module(FileManager.IDENTITY).upload_available.connect(update_file_upload_status); // Headerbar plugins app.plugin_registry.register_contact_titlebar_entry(new MenuEntry(stream_interactor)); app.plugin_registry.register_contact_titlebar_entry(search_menu_entry); app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor)); app.plugin_registry.register_contact_titlebar_entry(new CallTitlebarEntry(stream_interactor)); foreach(var entry in app.plugin_registry.conversation_titlebar_entries) { Widget? button = entry.get_widget(Plugins.WidgetType.GTK4) as Widget; if (button == null) { continue; } titlebar.insert_button(button); } Shortcut shortcut = new Shortcut(new KeyvalTrigger(Key.U, ModifierType.CONTROL_MASK), new CallbackAction(() => { if (conversation == null) return false; stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { open_file_picker(); } }); return false; })); ((Gtk.Window)view.get_root()).add_shortcut(shortcut); } public void select_conversation(Conversation? conversation, bool default_initialize_conversation) { if (this.conversation != null) { conversation.notify["encryption"].disconnect(update_file_upload_status); } if (overlay_dialog != null) { overlay_dialog.destroy(); } this.conversation = conversation; // Set list model onto list view // Dino.Application app = GLib.Application.get_default() as Dino.Application; // var map_list_model = get_conversation_content_model(new ContentItemMetaModel(app.db, conversation, stream_interactor), stream_interactor); // NoSelection selection_model = new NoSelection(map_list_model); // view.list_view.set_model(selection_model); // view.at_current_content = true; conversation.notify["encryption"].connect(update_file_upload_status); chat_input_controller.set_conversation(conversation); update_conversation_display_name(); update_conversation_topic(); foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { e.set_conversation(conversation); } if (default_initialize_conversation) { view.conversation_frame.initialize_for_conversation(conversation); } update_file_upload_status.begin(); } public void unset_conversation() { conversation_display_name = null; conversation_topic = null; } private async void update_file_upload_status() { if (conversation == null) return; bool upload_available = yield stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation); chat_input_controller.set_file_upload_active(upload_available); if (upload_available && overlay_dialog == null) { if (drop_event_controller.widget == null) { view.add_controller(drop_event_controller); } } else { if (drop_event_controller.widget != null) { view.remove_controller(drop_event_controller); } } } private void update_conversation_display_name() { conversation_display_name = Util.get_conversation_display_name(stream_interactor, conversation); } private void update_conversation_topic(string? subtitle = null) { if (subtitle != null) { conversation_topic = Util.summarize_whitespaces_to_space(subtitle); } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { string? subject = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account); if (subject != null) { conversation_topic = Util.summarize_whitespaces_to_space(subject); } else { conversation_topic = null; } } else { conversation_topic = null; } } private async void on_clipboard_paste() { try { Clipboard clipboard = view.get_clipboard(); Gdk.Texture? texture = yield clipboard.read_texture_async(null); // TODO critical var file_name = Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png"); texture.save_to_png(file_name); open_send_file_overlay(File.new_for_path(file_name)); } catch (IOError.NOT_SUPPORTED e) { // Format not supported, ignore } } private bool on_drag_data_received(DropTarget target, Value val, double x, double y) { if (val.type() == typeof(File)) { open_send_file_overlay((File)val); return true; } return false; } private void open_file_picker() { FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); chooser.response.connect((response) => { if (response == ResponseType.ACCEPT) { open_send_file_overlay(File.new_for_path(chooser.get_file().get_path())); } }); chooser.show(); } private void open_send_file_overlay(File file) { FileInfo file_info; try { file_info = file.query_info("*", FileQueryInfoFlags.NONE); } catch (Error e) { return; } FileSendOverlay overlay = new FileSendOverlay(file, file_info); overlay.send_file.connect(() => send_file(file)); stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.begin(conversation, (_, res) => { HashMap limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.end(res); bool something_works = false; foreach (var limit in limits.values) { if (limit >= file_info.get_size()) { something_works = true; } } if (!something_works && limits.has_key(0)) { if (!something_works && file_info.get_size() > limits[0]) { overlay.set_file_too_large(); } } }); overlay.close.connect(() => { // We don't want drag'n'drop to be active while the overlay is active overlay_dialog = null; update_file_upload_status.begin(); }); view.add_overlay_dialog(overlay.get_widget()); overlay_dialog = overlay.get_widget(); update_file_upload_status.begin(); } private void send_file(File file) { stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation); } private bool forward_key_press_to_chat_input(EventControllerKey key_controller, uint keyval, uint keycode, Gdk.ModifierType state) { if (view.get_root().get_focus() is TextView) { return false; } // Don't forward / change focus on Control / Alt if (keyval == Gdk.Key.Control_L || keyval == Gdk.Key.Control_R || keyval == Gdk.Key.Alt_L || keyval == Gdk.Key.Alt_R) { return false; } // Don't forward / change focus on Control + ... if ((state & ModifierType.CONTROL_MASK) > 0) { return false; } return key_controller.forward(view.chat_input.chat_text_view.text_view); } } } dino-0.4.3/main/src/ui/file_send_overlay.vala0000644000000000000000000000616114452563620017646 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class FileSendOverlay { public signal void close(); public signal void send_file(); public Box main_box; public Button close_button; public Button send_button; public SizingBin file_widget_insert; public Label info_label; private bool can_send = true; public FileSendOverlay(File file, FileInfo file_info) { Builder builder = new Builder.from_resource("/im/dino/Dino/file_send_overlay.ui"); main_box = (Box) builder.get_object("main_box"); close_button = (Button) builder.get_object("close_button"); send_button = (Button) builder.get_object("send_button"); file_widget_insert = (SizingBin) builder.get_object("file_widget_insert"); info_label = (Label) builder.get_object("info_label"); close_button.clicked.connect(() => { do_close(); }); send_button.clicked.connect(() => { send_file(); do_close(); }); load_file_widget.begin(file, file_info); main_box.realize.connect(() => { if (can_send) { send_button.grab_focus(); } else { close_button.grab_focus(); } }); var key_events = new EventControllerKey(); key_events.key_pressed.connect((keyval) => { if (keyval == Gdk.Key.Escape) { do_close(); } return false; }); this.main_box.add_controller(key_events); } private async void load_file_widget(File file, FileInfo file_info) { string file_name = file_info.get_display_name(); string mime_type = file_info.get_content_type(); bool is_image = false; foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) { foreach (string supported_mime_type in pixbuf_format.get_mime_types()) { if (supported_mime_type == mime_type) { is_image = true; } } } Widget? widget = null; if (is_image) { FileImageWidget image_widget = new FileImageWidget(); try { yield image_widget.load_from_file(file, file_name); widget = image_widget; } catch (Error e) { } } if (widget == null) { FileDefaultWidget default_widget = new FileDefaultWidget(); default_widget.name_label.label = file_name; default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size()); widget = default_widget; } widget.set_parent(file_widget_insert); } public void set_file_too_large() { info_label.label= _("The file exceeds the server's maximum upload size."); Util.force_error_color(info_label); send_button.sensitive = false; can_send = false; } private void do_close() { this.close(); main_box.unparent(); main_box.destroy(); } public Widget get_widget() { return main_box; } } } dino-0.4.3/main/src/ui/global_search.vala0000644000000000000000000003435514452563620016750 0ustar rootrootusing Gee; using Gtk; using Pango; using Dino.Entities; namespace Dino.Ui { public class GlobalSearch { public signal void selected_item(MessageItem item); private StreamInteractor stream_interactor; private string search = ""; private int loaded_results = -1; private Mutex reloading_mutex = Mutex(); public Overlay overlay; public SearchEntry search_entry; public Label entry_number_label; public ScrolledWindow results_scrolled; public Box results_box; public Stack results_empty_stack; public Frame auto_complete_overlay; public ListBox auto_complete_list; private ArrayList auto_complete_children = new ArrayList(); private ArrayList results_box_children = new ArrayList(); public GlobalSearch(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; Builder builder = new Builder.from_resource("/im/dino/Dino/global_search.ui"); overlay = (Overlay) builder.get_object("overlay"); search_entry = (SearchEntry) builder.get_object("search_entry"); entry_number_label = (Label) builder.get_object("entry_number_label"); results_scrolled = (ScrolledWindow) builder.get_object("results_scrolled"); results_box = (Box) builder.get_object("results_box"); results_empty_stack = (Stack) builder.get_object("results_empty_stack"); auto_complete_overlay = (Frame) builder.get_object("auto_complete_overlay"); auto_complete_list = (ListBox) builder.get_object("auto_complete_list"); search_entry.search_changed.connect(() => { set_search(search_entry.text); }); search_entry.notify["text"].connect_after(update_auto_complete); search_entry.notify["cursor-position"].connect_after(update_auto_complete); results_scrolled.vadjustment.notify["value"].connect(on_scrolled_window_vadjustment_value); results_scrolled.vadjustment.notify["upper"].connect_after(on_scrolled_window_vadjustment_upper); var overlay_key_events = new EventControllerKey() { name = "dino-search-overlay-key-events" }; overlay_key_events.key_pressed.connect(on_key_pressed); overlay_key_events.key_released.connect(on_key_released); overlay.add_controller(overlay_key_events); } private void on_scrolled_window_vadjustment_value() { if (results_scrolled.vadjustment.upper - (results_scrolled.vadjustment.value + results_scrolled.vadjustment.page_size) < 100) { if (!reloading_mutex.trylock()) return; Gee.List new_messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search, loaded_results); if (new_messages.size == 0) { reloading_mutex.unlock(); return; } loaded_results += new_messages.size; append_messages(new_messages); } } private void on_scrolled_window_vadjustment_upper() { reloading_mutex.trylock(); reloading_mutex.unlock(); } private bool on_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state) { if (!auto_complete_overlay.visible) return false; if (keyval == Gdk.Key.Up) { var row = auto_complete_list.get_selected_row(); var index = row == null ? -1 : row.get_index() - 1; if (index == -1) index = (int)auto_complete_children.size - 1; auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); return true; } if (keyval == Gdk.Key.Down) { var row = auto_complete_list.get_selected_row(); var index = row == null ? 0 : row.get_index() + 1; if (index == auto_complete_children.size) index = 0; auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); return true; } if (keyval == Gdk.Key.Tab) { auto_complete_list.get_selected_row().activate(); return true; } // TODO: Handle cursor movement in results // TODO: Direct all keystrokes to text input return false; } private void on_key_released(uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval == Gdk.Key.Return) { auto_complete_list.get_selected_row().activate(); } } private void update_auto_complete() { Gee.List suggestions = stream_interactor.get_module(SearchProcessor.IDENTITY).suggest_auto_complete(search_entry.text, search_entry.cursor_position); auto_complete_overlay.visible = suggestions.size > 0; if (suggestions.size > 0) { // Remove current suggestions foreach (Widget widget in auto_complete_children) { auto_complete_list.remove(widget); } auto_complete_children.clear(); // Populate new suggestions foreach(SearchSuggestion suggestion in suggestions) { Builder builder = new Builder.from_resource("/im/dino/Dino/search_autocomplete.ui"); AvatarImage avatar = (AvatarImage)builder.get_object("image"); Label label = (Label)builder.get_object("label"); string display_name; if (suggestion.conversation.type_ == Conversation.Type.GROUPCHAT && !suggestion.conversation.counterpart.equals(suggestion.jid) || suggestion.conversation.type_ == Conversation.Type.GROUPCHAT_PM) { display_name = Util.get_participant_display_name(stream_interactor, suggestion.conversation, suggestion.jid); avatar.set_conversation_participant(stream_interactor, suggestion.conversation, suggestion.jid); } else { display_name = Util.get_conversation_display_name(stream_interactor, suggestion.conversation); avatar.set_conversation(stream_interactor, suggestion.conversation); } if (display_name != suggestion.jid.to_string()) { label.set_markup("%s %s".printf(Markup.escape_text(display_name), Markup.escape_text(suggestion.jid.to_string()))); } else { label.label = display_name; } ListBoxRow row = new ListBoxRow() { visible = true, can_focus = false }; row.set_child((Widget)builder.get_object("root")); row.activate.connect(() => { handle_suggestion(suggestion); }); auto_complete_list.append(row); auto_complete_children.add(row); } auto_complete_list.select_row(auto_complete_list.get_row_at_index(0)); } } private void handle_suggestion(SearchSuggestion suggestion) { search_entry.delete_text(suggestion.start_index, suggestion.end_index); int position = search_entry.cursor_position; search_entry.insert_text(suggestion.completion + " ", suggestion.completion.length + 1, ref position); search_entry.set_position(-1); } private void clear_search() { // Scroll to top results_scrolled.vadjustment.value = 0; foreach (Widget widget in results_box_children) { results_box.remove(widget); } results_box_children.clear(); loaded_results = 0; } private void set_search(string search) { clear_search(); this.search = search; if (get_keywords(search).is_empty) { results_empty_stack.set_visible_child_name("empty"); return; } Gee.List messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search); if (messages.size == 0) { results_empty_stack.set_visible_child_name("no-result"); } else { results_empty_stack.set_visible_child_name("results"); int match_count = messages.size < 10 ? messages.size : stream_interactor.get_module(SearchProcessor.IDENTITY).count_match_messages(search); entry_number_label.label = "" + n("%i search result", "%i search results", match_count).printf(match_count) + ""; loaded_results += messages.size; append_messages(messages); } } private void append_messages(Gee.List messages) { foreach (MessageItem item in messages) { Gee.List before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(item.conversation, item.message.time, item.message.id, 1); Gee.List after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(item.conversation, item.message.time, item.message.id, 1); Box context_box = new Box(Orientation.VERTICAL, 5); if (before_message != null && before_message.size > 0) { context_box.append(get_context_message_widget(before_message.first())); } Widget match_widget = get_match_message_widget(item); context_box.append(match_widget); if (after_message != null && after_message.size > 0) { context_box.append(get_context_message_widget(after_message.first())); } Label date_label = new Label(ConversationSummary.ConversationItemSkeleton.get_relative_time(item.time.to_local())) { xalign=0 }; date_label.add_css_class("dim-label"); string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation); string title = item.message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name); Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_start=7 }; header_box.append(new Label(@"$(Markup.escape_text(title))") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true }); header_box.append(date_label); Box result_box = new Box(Orientation.VERTICAL, 7); result_box.append(header_box); result_box.append(context_box); results_box.append(result_box); results_box_children.add(result_box); } } private Widget get_match_message_widget(MessageItem item) { Grid grid = get_skeleton(item); grid.margin_top = 3; grid.margin_bottom = 3; string text = item.message.body.replace("\n", "").replace("\r", ""); if (text.length > 200) { int index = text.index_of(search); if (index + search.length <= 100) { text = text.substring(0, 150) + " … " + text.substring(text.length - 50, 50); } else if (index >= text.length - 100) { text = text.substring(0, 50) + " … " + text.substring(text.length - 150, 150); } else { text = text.substring(0, 25) + " … " + text.substring(index - 50, 50) + text.substring(index, 100) + " … " + text.substring(text.length - 25, 25); } } Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true }; // Build regex containing all keywords string regex_str = "("; Gee.List keywords = get_keywords(Regex.escape_string(search.down())); bool first = true; foreach (string keyword in keywords) { if (first) { first = false; } else { regex_str += "|"; } regex_str += "\\b" + keyword; } regex_str += ")"; // Color the keywords string markup_text = ""; try { Regex highlight_regex = new Regex(regex_str, RegexCompileFlags.CASELESS); MatchInfo match_info; highlight_regex.match(text, 0, out match_info); int last_end = 0; for (; match_info.matches(); match_info.next()) { int start, end; match_info.fetch_pos(0, out start, out end); markup_text += Markup.escape_text(text[last_end:start]) + "" + Markup.escape_text(text[start:end]) + ""; last_end = end; } markup_text += Markup.escape_text(text[last_end:text.length]); } catch (RegexError e) { assert_not_reached(); } label.label = markup_text; grid.attach(label, 1, 1, 1, 1); Button button = new Button() { has_frame=false }; button.clicked.connect(() => { selected_item(item); }); button.child = grid; return button; } private Grid get_context_message_widget(MessageItem item) { Grid grid = get_skeleton(item); grid.margin_start = 7; Label label = new Label(item.message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0 }; grid.attach(label, 1, 1, 1, 1); grid.opacity = 0.55; return grid; } private Grid get_skeleton(MessageItem item) { AvatarImage image = new AvatarImage() { height=32, width=32, margin_end=7, valign=Align.START, allow_gray = false }; image.set_conversation_participant(stream_interactor, item.conversation, item.jid); Grid grid = new Grid() { row_homogeneous=false }; grid.attach(image, 0, 0, 1, 2); string display_name = Util.get_participant_display_name(stream_interactor, item.conversation, item.jid); Label name_label = new Label(display_name) { ellipsize=EllipsizeMode.END, xalign=0 }; name_label.attributes = new AttrList(); name_label.attributes.insert(attr_weight_new(Weight.BOLD)); grid.attach(name_label, 1, 0, 1, 1); return grid; } private static Gee.List get_keywords(string search_string) { Gee.List ret = new ArrayList(); foreach (string search in search_string.split(" ")) { bool is_filter = search.has_prefix("from:") || search.has_prefix("in:") || search.has_prefix("with:"); if (!is_filter && search != "") { ret.add(search); } } return ret; } public Widget get_widget() { return overlay; } } } dino-0.4.3/main/src/ui/main_window.vala0000644000000000000000000001741014452563620016467 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class MainWindow : Adw.Window { public signal void conversation_selected(Conversation conversation); public new string? title { get; set; } public string? subtitle { get; set; } public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder(); public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder(); public ConversationView conversation_view; public ConversationSelector conversation_selector; public ConversationTitlebar conversation_titlebar; public Widget conversation_list_titlebar; public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL }; public Adw.Leaflet leaflet; public Box left_box; public Box right_box; public Adw.Flap search_flap; public GlobalSearch global_search; private Stack stack = new Stack(); private Stack left_stack; private Stack right_stack; private StreamInteractor stream_interactor; private Database db; private Config config; class construct { var shortcut = new Shortcut(new KeyvalTrigger(Key.F, ModifierType.CONTROL_MASK), new CallbackAction((widget, args) => { ((MainWindow) widget).search_flap.reveal_flap = true; return false; })); add_shortcut(shortcut); } public MainWindow(Application application, StreamInteractor stream_interactor, Database db, Config config) { Object(application : application); this.stream_interactor = stream_interactor; this.db = db; this.config = config; this.title = "Dino"; this.add_css_class("dino-main"); ((Widget)this).realize.connect(restore_window_size); setup_unified(); setup_headerbar(); setup_stack(); } private void setup_unified() { Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui"); leaflet = (Adw.Leaflet) builder.get_object("leaflet"); box.append(leaflet); left_box = (Box) builder.get_object("left_box"); right_box = (Box) builder.get_object("right_box"); left_stack = (Stack) builder.get_object("left_stack"); right_stack = (Stack) builder.get_object("right_stack"); conversation_view = (ConversationView) builder.get_object("conversation_view"); search_flap = (Adw.Flap) builder.get_object("search_flap"); conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor); conversation_selector.conversation_selected.connect_after(() => leaflet.navigate(Adw.NavigationDirection.FORWARD)); Adw.Bin search_frame = (Adw.Bin) builder.get_object("search_frame"); global_search = new GlobalSearch(stream_interactor); search_frame.set_child(global_search.get_widget()); } private void setup_headerbar() { if (Util.use_csd()) { conversation_list_titlebar = get_conversation_list_titlebar_csd(); conversation_titlebar = new ConversationTitlebarCsd(); leaflet.bind_property("folded", conversation_list_titlebar, "show-end-title-buttons", BindingFlags.SYNC_CREATE); leaflet.bind_property("folded", conversation_titlebar.get_widget(), "show-start-title-buttons", BindingFlags.SYNC_CREATE); } else { Label title_label = new Label("Dino"); HeaderBar titlebar = new HeaderBar() { title_widget=title_label, show_title_buttons=true }; box.prepend(titlebar); conversation_list_titlebar = new ConversationListTitlebar(); conversation_titlebar = new ConversationTitlebarNoCsd(); } left_box.prepend(conversation_list_titlebar); right_box.prepend(conversation_titlebar.get_widget()); leaflet.notify["folded"].connect_after(() => conversation_titlebar.back_button_visible = leaflet.folded); conversation_titlebar.back_pressed.connect(() => leaflet.navigate(Adw.NavigationDirection.BACK)); } private void setup_stack() { stack.add_named(box, "main"); stack.add_named(welcome_placeholder, "welcome_placeholder"); stack.add_named(accounts_placeholder, "accounts_placeholder"); set_content(stack); } public enum StackState { CLEAN_START, NO_ACTIVE_ACCOUNTS, NO_ACTIVE_CONVERSATIONS, CONVERSATION } public void set_stack_state(StackState stack_state) { if (stack_state == StackState.CONVERSATION) { left_stack.set_visible_child_name("content"); right_stack.set_visible_child_name("content"); stack.set_visible_child_name("main"); } else if (stack_state == StackState.CLEAN_START || stack_state == StackState.NO_ACTIVE_ACCOUNTS) { if (stack_state == StackState.CLEAN_START) { stack.set_visible_child_name("welcome_placeholder"); } else if (stack_state == StackState.NO_ACTIVE_ACCOUNTS) { stack.set_visible_child_name("accounts_placeholder"); } } else if (stack_state == StackState.NO_ACTIVE_CONVERSATIONS) { stack.set_visible_child_name("main"); left_stack.set_visible_child_name("placeholder"); right_stack.set_visible_child_name("placeholder"); } } public void loop_conversations(bool backwards) { conversation_selector.loop_conversations(backwards); } public void restore_window_size() { Gdk.Display? display = Gdk.Display.get_default(); if (display != null) { Gdk.Surface? surface = get_surface(); Gdk.Monitor? monitor = display.get_monitor_at_surface(surface); if (monitor != null && config.window_width <= monitor.geometry.width && config.window_height <= monitor.geometry.height) { set_default_size(config.window_width, config.window_height); } } if (config.window_maximize) { maximize(); } ((Widget)this).unrealize.connect(() => { save_window_size(); config.window_maximize = this.maximized; }); } public void save_window_size() { if (this.maximized) return; Gdk.Display? display = get_display(); Gdk.Surface? surface = get_surface(); if (display != null && surface != null) { Gdk.Monitor monitor = display.get_monitor_at_surface(surface); // Only store if the values have changed and are reasonable-looking. if (config.window_width != default_width && default_width > 0 && default_width <= monitor.geometry.width) { config.window_width = default_width; } if (config.window_height != default_height && default_height > 0 && default_height <= monitor.geometry.height) { config.window_height = default_height; } } } } public class WelcomePlaceholder : MainWindowPlaceholder { public WelcomePlaceholder() { status_page.title = _("Welcome to Dino!"); status_page.description = _("Sign in or create an account to get started."); primary_button.label = _("Set up account"); primary_button.visible = true; } } public class NoAccountsPlaceholder : MainWindowPlaceholder { public NoAccountsPlaceholder() { status_page.title = _("No active accounts"); primary_button.label = _("Manage accounts"); primary_button.visible = true; } } [GtkTemplate (ui = "/im/dino/Dino/unified_window_placeholder.ui")] public class MainWindowPlaceholder : Box { [GtkChild] public unowned Adw.StatusPage status_page; [GtkChild] public unowned Button primary_button; [GtkChild] public unowned Button secondary_button; } } dino-0.4.3/main/src/ui/main_window_controller.vala0000644000000000000000000002135614452563620020736 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class MainWindowController : Object { private StreamInteractor stream_interactor; private Conversation? conversation; private Application app; private Database db; private MainWindow window; private ConversationViewController conversation_view_controller; public MainWindowController(Application application, StreamInteractor stream_interactor, Database db) { this.app = application; this.stream_interactor = stream_interactor; this.db = db; stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(check_unset_conversation); stream_interactor.account_removed.connect(check_unset_conversation); SimpleAction jump_to_conversatio_message_action = new SimpleAction("jump-to-conversation-message", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); jump_to_conversatio_message_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null || !this.conversation.equals(conversation)) return; int item_id = variant.get_child_value(1).get_int32(); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, item_id); select_conversation(conversation, false, false); window.conversation_view.conversation_frame.initialize_around_message(conversation, content_item); }); app.add_action(jump_to_conversatio_message_action); } public void set_window(MainWindow window) { this.window = window; this.conversation_view_controller = new ConversationViewController(window.conversation_view, window.conversation_titlebar, stream_interactor); conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_flap, "reveal-flap", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); window.search_flap.notify["reveal-flap"].connect(() => { if (window.search_flap.reveal_flap) { if (window.conversation_view.conversation_frame.conversation != null && window.global_search.search_entry.text == "") { reset_search_entry(); } window.global_search.search_entry.grab_focus(); window.global_search.search_entry.set_position((int)window.global_search.search_entry.text.length); } }); window.global_search.selected_item.connect((item) => { select_conversation(item.conversation, false, false); window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item); if (window.search_flap.folded) { close_search(); } }); window.welcome_placeholder.primary_button.clicked.connect(() => { ManageAccounts.AddAccountDialog dialog = new ManageAccounts.AddAccountDialog(stream_interactor, db); dialog.set_transient_for(app.get_active_window()); dialog.present(); }); window.accounts_placeholder.primary_button.clicked.connect(() => { app.activate_action("accounts", null); }); window.conversation_selector.conversation_selected.connect((conversation) => select_conversation(conversation)); // ConversationListModel list_model = new ConversationListModel(stream_interactor); // list_model.closed_conversation.connect((conversation) => { // print(@"closed $(conversation.counterpart.bare_jid)\n"); // stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); // }); // SingleSelection selection_model = new SingleSelection(list_model) { autoselect=false }; // selection_model.notify["selected-item"].connect(() => { // ConversationViewModel view_model = (ConversationViewModel) selection_model.selected_item; // if (view_model.conversation.equals(conversation)) return; // print(@"selected conversation $(view_model.conversation.counterpart)\n"); // select_conversation(view_model.conversation); // }); // window.conversation_list_view.set_model(selection_model); // print(list_model.get_n_items().to_string() + " " + selection_model.get_n_items().to_string() + "<<"); // print(selection_model.get_selected().to_string() + "<<"); // window.conversation_list_view.realize.connect(() => { // selection_model.set_selected(0); // }); Widget window_widget = ((Widget) window); EventControllerKey key_event_controller = new EventControllerKey(); window_widget.add_controller(key_event_controller); // TODO GTK4: Why doesn't this work with key_pressed signal key_event_controller.key_released.connect((keyval) => { if (keyval == Gdk.Key.Escape) { close_search(); } }); EventControllerFocus focus_event_controller = new EventControllerFocus(); window_widget.add_controller(focus_event_controller); focus_event_controller.enter.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation); }); focus_event_controller.leave.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation); }); window.conversation_selected.connect(conversation => select_conversation(conversation)); stream_interactor.account_added.connect((account) => { update_stack_state(true); }); stream_interactor.account_removed.connect((account) => { update_stack_state(); }); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(() => update_stack_state()); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state()); update_stack_state(); } public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { this.conversation = conversation; conversation_view_controller.select_conversation(conversation, default_initialize_conversation); stream_interactor.get_module(ChatInteraction.IDENTITY).on_conversation_selected(conversation); conversation.active = true; // only for conversation_selected window.conversation_selector.on_conversation_selected(conversation); // In case selection was not via ConversationSelector if (do_reset_search) { reset_search_entry(); } } private void check_unset_conversation() { if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { unset_conversation(); } } private void unset_conversation() { this.conversation = null; conversation_view_controller.unset_conversation(); foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { e.unset_conversation(); } } private void update_stack_state(bool know_exists = false) { ArrayList accounts = stream_interactor.get_accounts(); if (!know_exists && accounts.size == 0) { if (db.get_accounts().size == 0) { window.set_stack_state(MainWindow.StackState.CLEAN_START); } else { window.set_stack_state(MainWindow.StackState.NO_ACTIVE_ACCOUNTS); } } else if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { window.set_stack_state(MainWindow.StackState.NO_ACTIVE_CONVERSATIONS); } else { window.set_stack_state(MainWindow.StackState.CONVERSATION); } } private void reset_search_entry() { if (window.conversation_view.conversation_frame.conversation != null) { switch (conversation.type_) { case Conversation.Type.CHAT: case Conversation.Type.GROUPCHAT_PM: window.global_search.search_entry.text = @"with:$(conversation.counterpart) "; break; case Conversation.Type.GROUPCHAT: window.global_search.search_entry.text = @"in:$(conversation.counterpart) "; break; } } } private void close_search() { conversation_view_controller.search_menu_entry.button.active = false; } } } dino-0.4.3/main/src/ui/manage_accounts/0000755000000000000000000000000014452563620016433 5ustar rootrootdino-0.4.3/main/src/ui/manage_accounts/account_row.vala0000644000000000000000000000252514452563620021627 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ManageAccounts { [GtkTemplate (ui = "/im/dino/Dino/manage_accounts/account_row.ui")] public class AccountRow : Gtk.ListBoxRow { [GtkChild] public unowned AvatarImage image; [GtkChild] public unowned Label jid_label; [GtkChild] public unowned Image icon; public Account account; private StreamInteractor stream_interactor; public AccountRow(StreamInteractor stream_interactor, Account account) { this.stream_interactor = stream_interactor; this.account = account; image.set_conversation(stream_interactor, new Conversation(account.bare_jid, account, Conversation.Type.CHAT)); jid_label.set_label(account.bare_jid.to_string()); stream_interactor.connection_manager.connection_error.connect((account, error) => { if (account.equals(this.account)) { update_warning_icon(); } }); stream_interactor.connection_manager.connection_state_changed.connect((account, state) => { if (account.equals(this.account)) { update_warning_icon(); } }); } private void update_warning_icon() { ConnectionManager.ConnectionError? error = stream_interactor.connection_manager.get_error(account); icon.visible = (error != null); } } } dino-0.4.3/main/src/ui/manage_accounts/add_account_dialog.vala0000644000000000000000000004430414452563620023070 0ustar rootrootusing Gee; using Gtk; using Pango; using Dino.Entities; using Xmpp; namespace Dino.Ui.ManageAccounts { [GtkTemplate (ui = "/im/dino/Dino/manage_accounts/add_account_dialog.ui")] public class AddAccountDialog : Gtk.Dialog { public signal void added(Account account); [GtkChild] private unowned Stack stack; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Label notification_label; // Sign in - JID [GtkChild] private unowned Box sign_in_jid_box; [GtkChild] private unowned Label sign_in_jid_error_label; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Stack sign_in_jid_continue_stack; [GtkChild] private unowned Button sign_in_jid_continue_button; [GtkChild] private unowned Button sign_in_jid_serverlist_button; // Sign in - TLS error [GtkChild] private unowned Box sign_in_tls_box; [GtkChild] private unowned Label sign_in_tls_label; [GtkChild] private unowned Stack sign_in_password_continue_stack; [GtkChild] private unowned Button sign_in_tls_back_button; // Sign in - Password [GtkChild] private unowned Box sign_in_password_box; [GtkChild] private unowned Label sign_in_password_title; [GtkChild] private unowned Label sign_in_password_error_label; [GtkChild] private unowned Entry password_entry; [GtkChild] private unowned Button sign_in_password_continue_button; [GtkChild] private unowned Button sign_in_password_back_button; // Select Server [GtkChild] private unowned Box create_account_box; [GtkChild] private unowned Button login_button; [GtkChild] private unowned Stack select_server_continue_stack; [GtkChild] private unowned Button select_server_continue; [GtkChild] private unowned Label register_form_continue_label; [GtkChild] private unowned ListBox server_list_box; [GtkChild] private unowned Entry server_entry; // Register Form [GtkChild] private unowned Box register_box; [GtkChild] private unowned Label register_title; [GtkChild] private unowned Box form_box; [GtkChild] private unowned Button register_form_back; [GtkChild] private unowned Stack register_form_continue_stack; [GtkChild] private unowned Button register_form_continue; // Success [GtkChild] private unowned Box success_box; [GtkChild] private unowned Label success_description; [GtkChild] private unowned Button success_continue_button; private static string[] server_list = new string[]{ "5222.de", "jabber.fr", "movim.eu", "yax.im" }; private StreamInteractor stream_interactor; private Database db; private HashMap list_box_jids = new HashMap(); private Jid? server_jid = null; private Jid? login_jid = null; private Xep.InBandRegistration.Form? form = null; public AddAccountDialog(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.title = _("Add Account"); // Sign in - Jid Util.force_error_color(sign_in_jid_error_label); jid_entry.changed.connect(on_jid_entry_changed); sign_in_jid_continue_button.clicked.connect(on_sign_in_jid_continue_button_clicked); sign_in_jid_serverlist_button.clicked.connect(show_select_server); // Sign in - TLS error sign_in_tls_back_button.clicked.connect(show_sign_in_jid); // Sign in - Password Util.force_error_color(sign_in_password_error_label); password_entry.changed.connect(() => { sign_in_password_continue_button.set_sensitive(password_entry.text.length > 0); }); sign_in_password_continue_button.clicked.connect(on_sign_in_password_continue_button_clicked); sign_in_password_back_button.clicked.connect(show_sign_in_jid); // Select Server server_entry.changed.connect(() => { try { Jid jid = new Jid(server_entry.text); select_server_continue.sensitive = jid != null && jid.localpart == null && jid.resourcepart == null; } catch (InvalidJidError e) { select_server_continue.sensitive = false; } }); select_server_continue.clicked.connect(on_select_server_continue); login_button.clicked.connect(show_sign_in_jid); foreach (string server in server_list) { ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7 }); list_box_jids[list_box_row] = server; server_list_box.append(list_box_row); } // Register Form register_form_continue.clicked.connect(on_register_form_continue_clicked); register_form_back.clicked.connect(show_select_server); // Success success_continue_button.clicked.connect(() => close()); show_sign_in_jid(); } private void show_sign_in_jid() { sign_in_jid_box.visible = true; jid_entry.grab_focus(); stack.visible_child_name = "login_jid"; sign_in_tls_box.visible = false; sign_in_password_box.visible = false; create_account_box.visible = false; register_box.visible = false; success_box.visible = false; set_default_widget(sign_in_jid_continue_button); sign_in_jid_error_label.label = ""; jid_entry.sensitive = true; animate_window_resize(sign_in_jid_box); } private void show_tls_error(string domain, TlsCertificateFlags error_flags) { sign_in_tls_box.visible = true; stack.visible_child_name = "tls_error"; sign_in_jid_box.visible = false; sign_in_password_box.visible = false; create_account_box.visible = false; register_box.visible = false; success_box.visible = false; string error_desc = _("The server could not prove that it is %s.").printf("" + domain + ""); if (TlsCertificateFlags.UNKNOWN_CA in error_flags) { error_desc += " " + _("Its security certificate is not trusted by your operating system."); } else if (TlsCertificateFlags.BAD_IDENTITY in error_flags) { error_desc += " " + _("Its security certificate is issued to another domain."); } else if (TlsCertificateFlags.NOT_ACTIVATED in error_flags) { error_desc += " " + _("Its security certificate will only become valid in the future."); } else if (TlsCertificateFlags.EXPIRED in error_flags) { error_desc += " " + _("Its security certificate is expired."); } sign_in_tls_label.label = error_desc; animate_window_resize(sign_in_tls_box); } private void show_sign_in_password() { sign_in_password_box.visible = true; password_entry.grab_focus(); stack.visible_child_name = "login_password"; sign_in_jid_box.visible = false; sign_in_tls_box.visible = false; create_account_box.visible = false; register_box.visible = false; success_box.visible = false; set_default_widget(sign_in_password_continue_button); sign_in_password_error_label.label = ""; sign_in_password_title.label = _("Sign in to %s").printf(login_jid.to_string()); animate_window_resize(sign_in_password_box); } private void show_select_server() { server_entry.text = ""; server_entry.grab_focus(); set_default_widget(select_server_continue); server_list_box.row_activated.disconnect(on_server_list_row_activated); server_list_box.unselect_all(); server_list_box.row_activated.connect(on_server_list_row_activated); create_account_box.visible = true; stack.visible_child_name = "server"; sign_in_jid_box.visible = false; sign_in_tls_box.visible = false; register_box.visible = false; success_box.visible = false; animate_window_resize(create_account_box); } private void show_register_form() { register_box.visible = true; stack.visible_child_name = "form"; sign_in_jid_box.visible = false; sign_in_tls_box.visible = false; sign_in_password_box.visible = false; create_account_box.visible = false; success_box.visible = false; set_default_widget(register_form_continue); animate_window_resize(register_box); } private void show_success(Account account) { success_box.visible = true; stack.visible_child_name = "success"; sign_in_jid_box.visible = false; sign_in_tls_box.visible = false; sign_in_password_box.visible = false; create_account_box.visible = false; register_box.visible = false; success_description.label = _("You can now use the account %s.").printf("" + Markup.escape_text(account.bare_jid.to_string()) + ""); set_default_widget(success_continue_button); } private void on_jid_entry_changed() { try { login_jid = new Jid(jid_entry.text); if (login_jid.localpart != null && login_jid.resourcepart == null) { sign_in_jid_continue_button.sensitive = true; jid_entry.secondary_icon_name = null; } else { sign_in_jid_continue_button.sensitive = false; } } catch (InvalidJidError e) { sign_in_jid_continue_button.sensitive = false; } } private async void on_sign_in_jid_continue_button_clicked() { try { login_jid = new Jid(jid_entry.text); jid_entry.sensitive = false; sign_in_tls_label.label = ""; sign_in_jid_error_label.label = ""; sign_in_jid_continue_button.sensitive = false; sign_in_jid_continue_stack.visible_child_name = "spinner"; Register.ServerAvailabilityReturn server_status = yield Register.check_server_availability(login_jid); sign_in_jid_continue_stack.visible_child_name = "label"; sign_in_jid_continue_button.sensitive = true; if (server_status.available) { show_sign_in_password(); } else { jid_entry.sensitive = true; if (server_status.error_flags != null) { show_tls_error(login_jid.domainpart, server_status.error_flags); } else { sign_in_jid_error_label.label = _("Could not connect to %s").printf(login_jid.domainpart); } } } catch (InvalidJidError e) { warning("Invalid address from interface allowed login: %s", e.message); sign_in_jid_error_label.label = _("Invalid address"); } } private async void on_sign_in_password_continue_button_clicked() { string password = password_entry.text; Account account = new Account(login_jid, null, password, null); sign_in_password_continue_stack.visible_child_name = "spinner"; ConnectionManager.ConnectionError.Source? error = yield stream_interactor.get_module(Register.IDENTITY).add_check_account(account); sign_in_password_continue_stack.visible_child_name = "label"; if (error != null) { switch (error) { case ConnectionManager.ConnectionError.Source.SASL: sign_in_password_error_label.label = _("Wrong username or password"); break; default: sign_in_password_error_label.label = _("Something went wrong"); break; } } else { add_activate_account(account); show_success(account); } } private void on_select_server_continue() { try { server_jid = new Jid(server_entry.text); request_show_register_form.begin(server_jid); } catch (InvalidJidError e) { warning("Invalid address from interface allowed server: %s", e.message); display_notification(_("Invalid address")); } } private void on_server_list_row_activated(ListBox box, ListBoxRow row) { try { server_jid = new Jid(list_box_jids[row]); request_show_register_form.begin(server_jid); } catch (InvalidJidError e) { warning("Invalid address from selected server: %s", e.message); display_notification(_("Invalid address")); } } private async void request_show_register_form(Jid server_jid) { select_server_continue_stack.visible_child_name = "spinner"; Register.RegistrationFormReturn form_return = yield Register.get_registration_form(server_jid); if (select_server_continue_stack == null) { return; } select_server_continue_stack.visible_child_name = "label"; if (form_return.form != null) { form = form_return.form; set_register_form(server_jid, form); show_register_form(); } else if (form_return.error_flags != null) { show_tls_error(server_jid.domainpart, form_return.error_flags); } else { display_notification(_("No response from server")); } } private void set_register_form(Jid server, Xep.InBandRegistration.Form form) { Widget widget = form_box.get_first_child(); while (widget != null) { form_box.remove(widget); widget = form_box.get_first_child(); } register_title.label = _("Register on %s").printf(server.to_string()); if (form.oob != null) { form_box.append(new Label(_("The server requires to sign up through a website"))); form_box.append(new Label(@"$(form.oob)") { use_markup=true }); register_form_continue_label.label = _("Open website"); register_form_continue.visible = true; register_form_continue.grab_focus(); } else if (form.fields.size > 0) { if (form.instructions != null && form.instructions != "") { string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false); form_box.append(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR }); } foreach (Xep.DataForms.DataForm.Field field in form.fields) { Widget? field_widget = Util.get_data_form_field_widget(field); if (field.label != null && field.label != "" && field_widget != null) { form_box.append(new Label(field.label) { xalign=0, margin_top=7 }); form_box.append(field_widget); } else if (field.type_ == Xep.DataForms.DataForm.Type.FIXED && field.get_value_string() != "") { string markup_fixed_field = Util.parse_add_markup(field.get_value_string(), null, true, false); form_box.append(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR }); } } register_form_continue.visible = true; register_form_continue_label.label = _("Register"); } else { form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"$(server)")) { use_markup=true }); register_form_continue.visible = false; } } private async void on_register_form_continue_clicked() { notification_revealer.set_reveal_child(false); // Button is opening a registration website if (form.oob != null) { try { AppInfo.launch_default_for_uri(form.oob, null); } catch (Error e) { } show_sign_in_jid(); return; } register_form_continue_stack.visible_child_name = "spinner"; string? error = yield Register.submit_form(server_jid, form); if (register_form_continue_stack == null) { return; } register_form_continue_stack.visible_child_name = "label"; if (error == null) { string? username = null, password = null; foreach (Xep.DataForms.DataForm.Field field in form.fields) { switch (field.var) { case "username": username = field.get_value_string(); break; case "password": password = field.get_value_string(); break; } } try { Account account = new Account(new Jid.components(username, server_jid.domainpart, null), null, password, null); add_activate_account(account); show_success(account); } catch (InvalidJidError e) { warning("Invalid address from components of registration: %s", e.message); display_notification(_("Invalid address")); } } else { display_notification(error); } } private void display_notification(string text) { notification_label.label = text; notification_revealer.set_reveal_child(true); Timeout.add_seconds(5, () => { notification_revealer.set_reveal_child(false); return false; }); } private void add_activate_account(Account account) { account.enabled = true; account.persist(db); stream_interactor.connect_account(account); added(account); } private void animate_window_resize(Widget widget) { // TODO code duplication int curr_height = widget.get_size(Orientation.VERTICAL); var natural_size = Requisition(); stack.get_preferred_size(null, out natural_size); int difference = natural_size.height + 5 - curr_height; Timer timer = new Timer(); Timeout.add((int) (stack.transition_duration / 30), () => { ulong microsec; timer.elapsed(out microsec); ulong millisec = microsec / 1000; double partial = double.min(1, (double) millisec / stack.transition_duration); default_height = (int) (curr_height + difference * partial); return millisec < stack.transition_duration; }); } } } dino-0.4.3/main/src/ui/manage_accounts/dialog.vala0000644000000000000000000002344714452563620020551 0ustar rootrootusing Gdk; using Gee; using Gtk; using Markup; using Dino.Entities; using Xmpp; namespace Dino.Ui.ManageAccounts { [GtkTemplate (ui = "/im/dino/Dino/manage_accounts/dialog.ui")] public class Dialog : Gtk.Dialog { public signal void account_enabled(Account account); public signal void account_disabled(Account account); [GtkChild] public unowned Stack main_stack; [GtkChild] public unowned ListBox account_list; [GtkChild] public unowned Button no_accounts_add; [GtkChild] public unowned Button add_account_button; [GtkChild] public unowned Button remove_account_button; [GtkChild] public unowned AvatarImage image; [GtkChild] public unowned Button image_button; [GtkChild] public unowned Label jid_label; [GtkChild] public unowned Label state_label; [GtkChild] public unowned Switch active_switch; [GtkChild] public unowned Util.EntryLabelHybrid password_hybrid; [GtkChild] public unowned Util.EntryLabelHybrid alias_hybrid; [GtkChild] public unowned Grid settings_list; private Database db; private StreamInteractor stream_interactor; private Account? selected_account; construct { Util.force_error_color(state_label, ".is_error"); account_list.row_selected.connect(on_account_list_row_selected); add_account_button.clicked.connect(show_add_account_dialog); no_accounts_add.clicked.connect(show_add_account_dialog); remove_account_button.clicked.connect(() => { AccountRow? account_row = account_list.get_selected_row() as AccountRow; if (selected_account != null) remove_account(account_row); }); image_button.clicked.connect(show_select_avatar); alias_hybrid.entry.changed.connect(() => { selected_account.alias = alias_hybrid.text; }); password_hybrid.entry.changed.connect(() => { selected_account.password = password_hybrid.text; }); Util.LabelHybridGroup label_hybrid_group = new Util.LabelHybridGroup(); label_hybrid_group.add(alias_hybrid); label_hybrid_group.add(password_hybrid); main_stack.set_visible_child_name("no_accounts"); int row_index = 4; int16 default_top_padding = new Gtk.Button().get_style_context().get_padding().top + 1; Application app = GLib.Application.get_default() as Application; foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) { Widget? widget = e.get_widget(Plugins.WidgetType.GTK4) as Widget; if (widget == null) continue; Label label = new Label(e.name) { xalign=1, yalign=0 }; label.add_css_class("dim-label"); label.margin_top = e.label_top_padding == -1 ? default_top_padding : e.label_top_padding; settings_list.attach(label, 0, row_index); settings_list.attach(widget, 1, row_index, 2); row_index++; } } public Dialog(StreamInteractor stream_interactor, Database db) { Object(use_header_bar : Util.use_csd() ? 1 : 0); this.db = db; this.stream_interactor = stream_interactor; foreach (Account account in db.get_accounts()) { add_account(account); } stream_interactor.get_module(AvatarManager.IDENTITY).received_avatar.connect(on_received_avatar); stream_interactor.connection_manager.connection_error.connect((account, error) => { if (account.equals(selected_account)) { update_status_label(account); } }); stream_interactor.connection_manager.connection_state_changed.connect((account, state) => { if (account.equals(selected_account)) { update_status_label(account); } }); if (account_list.get_row_at_index(0) != null) account_list.select_row(account_list.get_row_at_index(0)); } public AccountRow add_account(Account account) { AccountRow account_item = new AccountRow (stream_interactor, account); account_list.append(account_item); main_stack.set_visible_child_name("accounts_exist"); return account_item; } private void show_add_account_dialog() { AddAccountDialog add_account_dialog = new AddAccountDialog(stream_interactor, db); add_account_dialog.set_transient_for(this); add_account_dialog.added.connect((account) => { AccountRow account_item = add_account(account); account_list.select_row(account_item); account_list.queue_draw(); }); add_account_dialog.present(); } private void remove_account(AccountRow account_item) { Gtk.MessageDialog msg = new Gtk.MessageDialog ( this, Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("Remove account %s?"), account_item.jid_label.get_text()); msg.secondary_text = "You won't be able to access your conversation history anymore."; // TODO remove history! Button ok_button = msg.get_widget_for_response(ResponseType.OK) as Button; ok_button.label = _("Remove"); ok_button.add_css_class("destructive-action"); msg.response.connect((response) => { if (response == ResponseType.OK) { account_list.remove(account_item); if (account_item.account.enabled) account_disabled(account_item.account); account_item.account.remove(); if (account_list.get_row_at_index(0) != null) { account_list.select_row(account_list.get_row_at_index(0)); } else { main_stack.set_visible_child_name("no_accounts"); } } msg.close(); }); msg.present(); } private void on_account_list_row_selected(ListBoxRow? row) { AccountRow? account_item = row as AccountRow; if (account_item != null) { selected_account = account_item.account; populate_grid_data(account_item.account); } } private void show_select_avatar() { FileChooserNative chooser = new FileChooserNative(_("Select avatar"), this, FileChooserAction.OPEN, _("Select"), _("Cancel")); FileFilter filter = new FileFilter(); foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) { foreach (string mime_type in pixbuf_format.get_mime_types()) { filter.add_mime_type(mime_type); } } filter.set_filter_name(_("Images")); chooser.add_filter(filter); filter = new FileFilter(); filter.set_filter_name(_("All files")); filter.add_pattern("*"); chooser.add_filter(filter); chooser.response.connect(() => { string uri = chooser.get_file().get_path(); stream_interactor.get_module(AvatarManager.IDENTITY).publish(selected_account, uri); }); chooser.show(); } private bool change_account_state(bool state) { selected_account.enabled = state; if (state) { account_enabled(selected_account); } else { account_disabled(selected_account); } return false; } private void on_received_avatar(Jid jid, Account account) { if (selected_account.equals(account) && jid.equals(account.bare_jid)) { image.set_conversation(stream_interactor, new Conversation(account.bare_jid, account, Conversation.Type.CHAT)); } } private void populate_grid_data(Account account) { active_switch.state_set.disconnect(change_account_state); image.set_conversation(stream_interactor, new Conversation(account.bare_jid, account, Conversation.Type.CHAT)); active_switch.set_active(account.enabled); jid_label.label = account.bare_jid.to_string(); alias_hybrid.text = account.alias ?? ""; password_hybrid.entry.input_purpose = InputPurpose.PASSWORD; password_hybrid.text = account.password; update_status_label(account); active_switch.state_set.connect(change_account_state); Application app = GLib.Application.get_default() as Application; foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) { e.set_account(account); } } private void update_status_label(Account account) { state_label.label = ""; ConnectionManager.ConnectionError? error = stream_interactor.connection_manager.get_error(account); if (error != null) { state_label.label = get_connection_error_description(error); state_label.add_css_class("is_error"); } else { ConnectionManager.ConnectionState state = stream_interactor.connection_manager.get_state(account); switch (state) { case ConnectionManager.ConnectionState.CONNECTING: state_label.label = _("Connecting…"); break; case ConnectionManager.ConnectionState.CONNECTED: state_label.label = _("Connected"); break; case ConnectionManager.ConnectionState.DISCONNECTED: state_label.label = _("Disconnected"); break; } state_label.remove_css_class("is_error"); } } private string get_connection_error_description(ConnectionManager.ConnectionError error) { switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: return _("Wrong password"); case ConnectionManager.ConnectionError.Source.TLS: return _("Invalid TLS certificate"); } if (error.identifier != null) { return _("Error") + ": " + error.identifier; } else { return _("Error"); } } } } dino-0.4.3/main/src/ui/notifier_freedesktop.vala0000644000000000000000000003650514452563620020374 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; public class Dino.Ui.FreeDesktopNotifier : NotificationProvider, Object { public signal void conversation_selected(Conversation conversation); private StreamInteractor stream_interactor; private DBusNotifications dbus_notifications; private bool supports_body_markup = false; private bool supports_body_hyperlinks = false; private HashMap content_notifications = new HashMap(Conversation.hash_func, Conversation.equals_func); private HashMap> conversation_notifications = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap> action_listeners = new HashMap>(); private HashMap call_notifications = new HashMap(Call.hash_func, Call.equals_func); public FreeDesktopNotifier(StreamInteractor stream_interactor, DBusNotifications dbus_notifications) { this.stream_interactor = stream_interactor; this.dbus_notifications = dbus_notifications; init_dbus_notifications.begin(); } private async void init_dbus_notifications() throws Error { string[] caps; yield dbus_notifications.get_capabilities(out caps); foreach (string cap in caps) { switch (cap) { case "body-markup": supports_body_markup = true; break; case "body-hyperlinks": supports_body_hyperlinks = true; break; } } dbus_notifications.action_invoked.connect((id, action) => { if (action_listeners.has_key(id) && action_listeners[id].has_key(action)) { action_listeners[id][action].func(); } }); dbus_notifications.notification_closed.connect((id) => { action_listeners.unset(id); }); } public double get_priority() { return 1; } public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) { string body = ""; if (supports_body_hyperlinks) { body = Util.parse_add_markup(message.body, null, true, false); } else if (supports_body_markup) { body = Markup.escape_text(message.body); } else { body = message.body; } yield notify_content_item(conversation, conversation_display_name, participant_display_name, body); } public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) { string text = ""; if (file_transfer.direction == Message.DIRECTION_SENT) { text = is_image ? _("Image sent") : _("File sent"); } else { text = is_image ? _("Image received") : _("File received"); } if (supports_body_markup) { text = "" + text + ""; } yield notify_content_item(conversation, conversation_display_name, participant_display_name, text); } private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) { string body = body_; if (participant_display_name != null) { if (supports_body_markup) { body = @"$(Markup.escape_text(participant_display_name)): $body"; } else { body = @"$participant_display_name: $body"; } } uint32 replace_id = content_notifications.has_key(conversation) ? content_notifications[conversation] : 0; HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); string[] actions = new string[] {"default", "Open conversation"}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", replace_id, "", conversation_display_name, body, actions, hash_table, -1); content_notifications[conversation] = notification_id; add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing content item notification: %s", e.message); } } public async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name) { debug("[%s] Call notification", call.account.bare_jid.to_string()); string summary = Markup.escape_text(conversation_display_name); string body = video ? _("Incoming video call") : _("Incoming call"); if (multiparty) { body = video ? _("Incoming video group call") : _("Incoming group call"); } HashTable hash_table = new HashTable(null, null); hash_table["image-path"] = "call-start-symbolic"; hash_table["sound-name"] = new Variant.string("phone-incoming-call"); hash_table["urgency"] = new Variant.byte(2); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); string[] actions = new string[] {"default", "Open conversation", "reject", _("Reject"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0); call_notifications[call] = notification_id; add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "reject", () => { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.int32(call.id)}); GLib.Application.get_default().activate_action("reject-call", variant); }); add_action_listener(notification_id, "accept", () => { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.int32(call.id)}); GLib.Application.get_default().activate_action("accept-call", variant); }); } catch (Error e) { warning("Failed showing subscription request notification: %s", e.message); } } public async void retract_call_notification(Call call, Conversation conversation) { if (!call_notifications.has_key(call)) return; uint32 notification_id = call_notifications[call]; try { yield dbus_notifications.close_notification(notification_id); action_listeners.unset(notification_id); call_notifications.unset(call); } catch (Error e) { } } public async void notify_subscription_request(Conversation conversation) { string summary = _("Subscription request"); string body = Markup.escape_text(conversation.counterpart.to_string()); HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); string[] actions = new string[] {"default", "Open conversation", "accept", _("Accept"), "deny", _("Deny")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); if (!conversation_notifications.has_key(conversation)) { conversation_notifications[conversation] = new ArrayList(); } conversation_notifications[conversation].add(notification_id); add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("accept-subscription", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-subscription", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing subscription request notification: %s", e.message); } } public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) { string summary = _("Could not connect to %s").printf(account.bare_jid.domainpart); string body = ""; switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: body = _("Wrong password"); break; case ConnectionManager.ConnectionError.Source.TLS: body = _("Invalid TLS certificate"); break; default: break; } HashTable hash_table = new HashTable(null, null); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); try { yield dbus_notifications.notify("Dino", 0, "im.dino.Dino", summary, body, new string[]{}, hash_table, -1); } catch (Error e) { warning("Failed showing connection error notification: %s", e.message); } } public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); string display_room = room_jid.bare_jid.to_string(); string summary = _("Invitation to %s").printf(display_room); string body = _("%s invited you to %s").printf(inviter_display_name, display_room); if (supports_body_markup) { body = Markup.escape_text(body); } HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(direct_conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); string[] actions = new string[] {"default", "", "reject", _("Reject"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); }); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-invite", new Variant.int32(group_conversation.id)); }); } catch (Error e) { warning("Failed showing muc invite notification: %s", e.message); } } public async void notify_voice_request(Conversation conversation, Jid from_jid) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, from_jid); string display_room = Util.get_conversation_display_name(stream_interactor, conversation); string summary = _("Permission request"); string body = _("%s requests the permission to write in %s").printf(display_name, display_room); if (supports_body_markup) { Markup.escape_text(body); } HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); string[] actions = new string[] {"deny", _("Deny"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("accept-voice-request", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-voice-request", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing voice request notification: %s", e.message); } } public async void retract_content_item_notifications() { if (content_notifications != null) { foreach (uint32 id in content_notifications.values) { try { dbus_notifications.close_notification.begin(id); } catch (Error e) { } } content_notifications.clear(); } } public async void retract_conversation_notifications(Conversation conversation) { if (content_notifications.has_key(conversation)) { try { dbus_notifications.close_notification.begin(content_notifications[conversation]); } catch (Error e) { } } content_notifications.unset(conversation); } private async Variant get_conversation_icon(Conversation conversation) { AvatarDrawer drawer = yield Util.get_conversation_avatar_drawer(stream_interactor, conversation); Cairo.ImageSurface surface = drawer.size(40, 40).draw_image_surface(); Gdk.Pixbuf avatar = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()); var bytes = avatar.pixel_bytes; var image_bytes = Variant.new_from_data(new VariantType("ay"), bytes.get_data(), true, bytes); return new Variant("(iiibii@ay)", avatar.width, avatar.height, avatar.rowstride, avatar.has_alpha, avatar.bits_per_sample, avatar.n_channels, image_bytes); } private void add_action_listener(uint32 id, string name, owned ListenerFunc func) { if (!action_listeners.has_key(id)) { action_listeners[id] = new HashMap(); } action_listeners[id][name] = new ListenerFuncWrapper((owned) func); } delegate void ListenerFunc(); class ListenerFuncWrapper { public ListenerFunc func; public ListenerFuncWrapper(owned ListenerFunc func) { this.func = (owned) func; } } } dino-0.4.3/main/src/ui/notifier_gnotifications.vala0000644000000000000000000002275114452563620021077 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class GNotificationsNotifier : NotificationProvider, Object { public signal void conversation_selected(Conversation conversation); private StreamInteractor stream_interactor; private HashMap notifications = new HashMap(Conversation.hash_func, Conversation.equals_func); private Set? active_conversation_ids = null; private Set? active_ids = new HashSet(); public GNotificationsNotifier(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public double get_priority() { return 0; } public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) { string text = message.body; if (participant_display_name != null) { text = @"$participant_display_name: $text"; } yield notify_content_item(conversation, conversation_display_name, text); } public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) { string text = ""; if (file_transfer.direction == Message.DIRECTION_SENT) { text = is_image ? _("Image sent") : _("File sent"); } else { text = is_image ? _("Image received") : _("File received"); } if (participant_display_name != null) { text = @"$participant_display_name: $text"; } yield notify_content_item(conversation, conversation_display_name, text); } private async void notify_content_item(Conversation conversation, string title, string body) { if (!notifications.has_key(conversation)) { notifications[conversation] = new Notification(""); notifications[conversation].set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); } Notification notification = notifications[conversation]; notification.set_title(title); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } GLib.Application.get_default().send_notification(conversation.id.to_string(), notifications[conversation]); if (active_conversation_ids != null) { active_conversation_ids.add(conversation.id.to_string()); } } public async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name) { Notification notification = new Notification(conversation_display_name); string body = video ? _("Incoming video call") : _("Incoming call"); if (multiparty) { body = video ? _("Incoming video group call") : _("Incoming group call"); } notification.set_body(body); notification.set_urgent(true); notification.set_icon(new ThemedIcon.from_names(new string[] {"call-start-symbolic"})); notification.set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); notification.add_button_with_target_value(_("Reject"), "app.reject-call", new Variant.int32(call.id)); notification.add_button_with_target_value(_("Accept"), "app.accept-call", new Variant.int32(call.id)); GLib.Application.get_default().send_notification(call.id.to_string(), notification); } private async void retract_call_notification(Call call, Conversation conversation) { GLib.Application.get_default().withdraw_notification(call.id.to_string()); } public async void notify_subscription_request(Conversation conversation) { Notification notification = new Notification(_("Subscription request")); notification.set_body(conversation.counterpart.to_string()); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } notification.set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); notification.add_button_with_target_value(_("Accept"), "app.accept-subscription", conversation.id); notification.add_button_with_target_value(_("Deny"), "app.deny-subscription", conversation.id); GLib.Application.get_default().send_notification(conversation.id.to_string() + "-subscription", notification); active_ids.add(conversation.id.to_string() + "-subscription"); } public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) { Notification notification = new Notification(_("Could not connect to %s").printf(account.bare_jid.domainpart)); switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: notification.set_body("Wrong password"); break; case ConnectionManager.ConnectionError.Source.TLS: notification.set_body("Invalid TLS certificate"); break; } GLib.Application.get_default().send_notification(account.id.to_string() + "-connection-error", notification); } public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); string display_room = room_jid.bare_jid.to_string(); Notification notification = new Notification(_("Invitation to %s").printf(display_room)); string body = _("%s invited you to %s").printf(inviter_display_name, display_room); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(direct_conversation)); } catch (Error e) { } Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); notification.set_default_action_and_target_value("app.open-muc-join", new Variant.int32(group_conversation.id)); notification.add_button_with_target_value(_("Deny"), "app.deny-invite", group_conversation.id); notification.add_button_with_target_value(_("Accept"), "app.open-muc-join", group_conversation.id); GLib.Application.get_default().send_notification(null, notification); } public async void notify_voice_request(Conversation conversation, Jid from_jid) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, from_jid); string display_room = Util.get_conversation_display_name(stream_interactor, conversation); Notification notification = new Notification(_("Permission request")); string body = _("%s requests the permission to write in %s").printf(display_name, display_room); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } notification.add_button_with_target_value(_("Deny"), "app.deny-voice-request", conversation.id); notification.add_button_with_target_value(_("Accept"), "app.accept-voice-request", conversation.id); GLib.Application.get_default().send_notification(null, notification); } public async void retract_content_item_notifications() { if (active_conversation_ids == null) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(); foreach (Conversation conversation in conversations) { GLib.Application.get_default().withdraw_notification(conversation.id.to_string()); } active_conversation_ids = new HashSet(); } else { foreach (string id in active_conversation_ids) { GLib.Application.get_default().withdraw_notification(id); } active_conversation_ids.clear(); } } public async void retract_conversation_notifications(Conversation conversation) { string subscription_id = conversation.id.to_string() + "-subscription"; if (active_ids.contains(subscription_id)) { GLib.Application.get_default().withdraw_notification(subscription_id); } } private async Icon get_conversation_icon(Conversation conversation) throws Error { AvatarDrawer drawer = yield Util.get_conversation_avatar_drawer(stream_interactor, conversation); Cairo.ImageSurface surface = drawer.size(40, 40).draw_image_surface(); Gdk.Pixbuf avatar = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()); uint8[] buffer; avatar.save_to_buffer(out buffer, "png"); return new BytesIcon(new Bytes(buffer)); } } } dino-0.4.3/main/src/ui/occupant_menu/0000755000000000000000000000000014452563620016144 5ustar rootrootdino-0.4.3/main/src/ui/occupant_menu/list.vala0000644000000000000000000001535714452563620017777 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { [GtkTemplate (ui = "/im/dino/Dino/occupant_list.ui")] public class List : Box { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; [GtkChild] public unowned ListBox list_box; [GtkChild] private unowned SearchEntry search_entry; private Conversation conversation; private string[]? filter_values; private HashMap rows = new HashMap(Jid.hash_func, Jid.equals_func); public HashMap row_wrappers = new HashMap(); public List(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; list_box.set_header_func(header); list_box.set_sort_func(sort); list_box.set_filter_func(filter); search_entry.search_changed.connect(refilter); stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_show_received); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_received_offline_presence); initialize_for_conversation(conversation); } public void initialize_for_conversation(Conversation conversation) { this.conversation = conversation; Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) { foreach (Jid occupant in occupants) { add_occupant(occupant); } } list_box.invalidate_filter(); } private void refilter() { string[]? values = null; string str = search_entry.get_text (); if (str != "") values = str.split(" "); if (filter_values == values) return; filter_values = values; list_box.invalidate_filter(); } public void add_occupant(Jid jid) { var row_wrapper = new ListRow(stream_interactor, conversation, jid); var widget = row_wrapper.get_widget(); row_wrappers[widget] = row_wrapper; rows[jid] = widget; list_box.append(widget); } public void remove_occupant(Jid jid) { list_box.remove(rows[jid]); rows.unset(jid); } private void on_received_offline_presence(Jid jid, Account account) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (rows.has_key(jid)) { remove_occupant(jid); } list_box.invalidate_filter(); } } private void on_show_received(Jid jid, Account account) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (!rows.has_key(jid)) { add_occupant(jid); } list_box.invalidate_filter(); } } private void header(ListBoxRow row, ListBoxRow? before_row) { ListRow row_wrapper1 = row_wrappers[row.get_child()]; Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account); if (a1 == null) return; if (before_row != null) { ListRow row_wrapper2 = row_wrappers[before_row.get_child()]; Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account); if (a1 != a2) { row.set_header(generate_header_widget(a1, false)); } else if (row.get_header() != null){ row.set_header(null); } } else { row.set_header(generate_header_widget(a1, true)); } } private Widget generate_header_widget(Xmpp.Xep.Muc.Affiliation affiliation, bool top) { string aff_str; switch (affiliation) { case Xmpp.Xep.Muc.Affiliation.OWNER: aff_str = _("Owner"); break; case Xmpp.Xep.Muc.Affiliation.ADMIN: aff_str = _("Admin"); break; case Xmpp.Xep.Muc.Affiliation.MEMBER: aff_str = _("Member"); break; default: aff_str = _("User"); break; } int count = 0; foreach (ListRow row in row_wrappers.values) { Xmpp.Xep.Muc.Affiliation aff = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row.jid, conversation.account); if (aff == affiliation) count++; } Label title_label = new Label("") { margin_start=10, xalign=0 }; title_label.set_markup(@"$(Markup.escape_text(aff_str))"); Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true }; count_label.add_css_class("dim-label"); Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true }; grid.attach(title_label, 0, 0, 1, 1); grid.attach(count_label, 1, 0, 1, 1); grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true }, 0, 1, 2, 1); return grid; } private bool filter(ListBoxRow r) { ListRow row_wrapper = row_wrappers[r.get_child()]; foreach (string filter in filter_values) { return row_wrapper.name_label.label.down().contains(filter.down()); } return true; } private int sort(ListBoxRow row1, ListBoxRow row2) { ListRow row_wrapper1 = row_wrappers[row1.get_child()]; ListRow row_wrapper2 = row_wrappers[row2.get_child()]; int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); if (affiliation1 < affiliation2) return -1; else if (affiliation1 > affiliation2) return 1; else return row_wrapper1.name_label.label.collate(row_wrapper2.name_label.label); return 0; } private int get_affiliation_ranking(Xmpp.Xep.Muc.Affiliation affiliation) { switch (affiliation) { case Xmpp.Xep.Muc.Affiliation.OWNER: return 1; case Xmpp.Xep.Muc.Affiliation.ADMIN: return 2; case Xmpp.Xep.Muc.Affiliation.MEMBER: return 3; default: return 4; } } } } dino-0.4.3/main/src/ui/occupant_menu/list_row.vala0000644000000000000000000000211014452563620020645 0ustar rootrootusing Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { public class ListRow : Object { private Grid main_grid; private AvatarImage image; public Label name_label; public Conversation? conversation; public Jid? jid; construct { Builder builder = new Builder.from_resource("/im/dino/Dino/occupant_list_item.ui"); main_grid = (Grid) builder.get_object("main_grid"); image = (AvatarImage) builder.get_object("image"); name_label = (Label) builder.get_object("name_label"); } public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) { this.conversation = conversation; this.jid = jid; name_label.label = Util.get_participant_display_name(stream_interactor, conversation, jid); image.set_conversation_participant(stream_interactor, conversation, jid); } public ListRow.label(string c, string text) { name_label.label = text; image.set_text(c); } public Widget get_widget() { return main_grid; } } } dino-0.4.3/main/src/ui/occupant_menu/view.vala0000644000000000000000000001456714452563620020000 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { public class View : Popover { private StreamInteractor stream_interactor; private Conversation conversation; private Stack stack = new Stack() { vhomogeneous=false }; private Box list_box = new Box(Orientation.VERTICAL, 1); private List? list = null; private ListBox invite_list = new ListBox(); private Box? jid_menu = null; private Jid? selected_jid; public View(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.show.connect(initialize_list); invite_list.append(new ListRow.label("+", _("Invite")).get_widget()); invite_list.can_focus = false; list_box.append(invite_list); invite_list.row_activated.connect(on_invite_clicked); stack.add_named(list_box, "list"); set_child(stack); stack.visible_child_name = "list"; hide.connect(reset); } public void reset() { stack.transition_type = StackTransitionType.NONE; stack.visible_child_name = "list"; if (list != null) list.list_box.unselect_all(); invite_list.unselect_all(); } private void initialize_list() { if (list == null) { list = new List(stream_interactor, conversation); list_box.prepend(list); list.list_box.row_activated.connect((row) => { ListRow row_wrapper = list.row_wrappers[row.get_child()]; show_menu(row_wrapper.jid, row_wrapper.name_label.label); }); } } private void show_list() { if (list != null) list.list_box.unselect_all(); stack.transition_type = StackTransitionType.SLIDE_RIGHT; stack.visible_child_name = "list"; } private void show_menu(Jid jid, string name_) { selected_jid = jid; stack.transition_type = StackTransitionType.SLIDE_LEFT; string name = Markup.escape_text(name_); Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, conversation.account); if (real_jid != null) name += "\n%s".printf(Markup.escape_text(real_jid.bare_jid.to_string())); Box header_box = new Box(Orientation.HORIZONTAL, 5); header_box.append(new Image.from_icon_name("pan-start-symbolic")); header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true }); Button header_button = new Button() { has_frame=false }; header_button.child = header_box; Box outer_box = new Box(Orientation.VERTICAL, 5); outer_box.append(header_button); header_button.clicked.connect(show_list); Button private_button = new Button.with_label(_("Start private conversation")) ; outer_box.append(private_button); private_button.clicked.connect(private_conversation_button_clicked); Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); Xmpp.Xep.Muc.Role? role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account); if (role == Xmpp.Xep.Muc.Role.MODERATOR && stream_interactor.get_module(MucManager.IDENTITY).kick_possible(conversation.account, jid)) { Button kick_button = new Button.with_label(_("Kick")) ; outer_box.append(kick_button); kick_button.clicked.connect(kick_button_clicked); } if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && role == Xmpp.Xep.Muc.Role.MODERATOR){ if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) { Button voice_button = new Button.with_label(_("Grant write permission")) ; outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("participant")); } else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){ Button voice_button = new Button.with_label(_("Revoke write permission")) ; outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("visitor")); } } if (jid_menu != null) stack.remove(jid_menu); stack.add_named(outer_box, "menu"); stack.visible_child_name = "menu"; jid_menu = outer_box; } private void private_conversation_button_clicked() { if (selected_jid == null) return; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(selected_jid, conversation.account, Conversation.Type.GROUPCHAT_PM); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); Application app = GLib.Application.get_default() as Application; app.controller.select_conversation(conversation); } private void kick_button_clicked() { if (selected_jid == null) return; stream_interactor.get_module(MucManager.IDENTITY).kick(conversation.account, conversation.counterpart, selected_jid.resourcepart); } private void voice_button_clicked(string role) { if (selected_jid == null) return; stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, selected_jid.resourcepart, role); } private void on_invite_clicked() { hide(); Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(conversation.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); add_chat_dialog.set_transient_for((Window) get_root()); add_chat_dialog.title = _("Invite to Conference"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { stream_interactor.get_module(MucManager.IDENTITY).invite(conversation.account, conversation.counterpart, jid); }); add_chat_dialog.present(); } } } dino-0.4.3/main/src/ui/settings_dialog.vala0000644000000000000000000000221414452563620017327 0ustar rootrootusing Gtk; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/settings_dialog.ui")] class SettingsDialog : Adw.PreferencesWindow { [GtkChild] private unowned Switch typing_switch; [GtkChild] private unowned Switch marker_switch; [GtkChild] private unowned Switch notification_switch; [GtkChild] private unowned Switch emoji_switch; Dino.Entities.Settings settings = Dino.Application.get_default().settings; public SettingsDialog() { Object(); typing_switch.active = settings.send_typing; marker_switch.active = settings.send_marker; notification_switch.active = settings.notifications; emoji_switch.active = settings.convert_utf8_smileys; typing_switch.notify["active"].connect(() => { settings.send_typing = typing_switch.active; } ); marker_switch.notify["active"].connect(() => { settings.send_marker = marker_switch.active; } ); notification_switch.notify["active"].connect(() => { settings.notifications = notification_switch.active; } ); emoji_switch.notify["active"].connect(() => { settings.convert_utf8_smileys = emoji_switch.active; }); } } } dino-0.4.3/main/src/ui/util/0000755000000000000000000000000014452563620014261 5ustar rootrootdino-0.4.3/main/src/ui/util/accounts_combo_box.vala0000644000000000000000000000317714452563620021004 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { class AccountComboBox : ComboBox { public Account? selected { get { TreeIter selected; if (get_active_iter(out selected)) { Value value; list_store.get_value(selected, 1, out value); return value as Account; } return null; } set { TreeIter iter; if (list_store.get_iter_first(out iter)) { int i = 0; do { Value val; list_store.get_value(iter, 1, out val); Account? account = val as Account; if (account != null && account.equals(value)) { active = i; break; } i++; } while (list_store.iter_next(ref iter)); } } } private StreamInteractor? stream_interactor; private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(Account)); public void initialize(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; CellRendererText renderer = new Gtk.CellRendererText(); pack_start(renderer, true); add_attribute(renderer, "text", 0); TreeIter iter; foreach (Account account in stream_interactor.get_accounts()) { list_store.append(out iter); list_store.set(iter, 0, account.bare_jid.to_string(), 1, account); } set_model(list_store); active = 0; } } } dino-0.4.3/main/src/ui/util/config.vala0000644000000000000000000000414314452563620016375 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { public class Config : Object { public Database db { get; private set; } public Config(Database db) { this.db = db; window_maximize = col_to_bool_or_default("window_maximized", false); window_width = col_to_int_or_default("window_width", 1200); window_height = col_to_int_or_default("window_height", 700); } private bool window_maximize_; public bool window_maximize { get { return window_maximize_; } set { if (value == window_maximize_) return; db.settings.upsert() .value(db.settings.key, "window_maximized", true) .value(db.settings.value, value.to_string()) .perform(); window_maximize_ = value; } } public int window_height_; public int window_height { get { return window_height_; } set { if (value == window_height_) return; db.settings.upsert() .value(db.settings.key, "window_height", true) .value(db.settings.value, value.to_string()) .perform(); window_height_ = value; } } public int window_width_; public int window_width { get { return window_width_; } set { if (value == window_width_) return; db.settings.upsert() .value(db.settings.key, "window_width", true) .value(db.settings.value, value.to_string()) .perform(); window_width_ = value; } } private bool col_to_bool_or_default(string key, bool def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? bool.parse(val) : def; } private int col_to_int_or_default(string key, int def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? int.parse(val) : def; } } } dino-0.4.3/main/src/ui/util/data_forms.vala0000644000000000000000000000443214452563620017250 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp.Xep; namespace Dino.Ui.Util { public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field) { if (field.type_ == null) return null; switch (field.type_) { case DataForms.DataForm.Type.BOOLEAN: DataForms.DataForm.BooleanField boolean_field = field as DataForms.DataForm.BooleanField; Switch sw = new Switch() { active=boolean_field.value, halign=Align.START, valign=Align.CENTER }; sw.state_set.connect((state) => { boolean_field.value = state; return false; }); return sw; case DataForms.DataForm.Type.JID_MULTI: return null; case DataForms.DataForm.Type.LIST_SINGLE: DataForms.DataForm.ListSingleField list_single_field = field as DataForms.DataForm.ListSingleField; ComboBoxText combobox = new ComboBoxText() { valign=Align.CENTER }; for (int i = 0; i < list_single_field.options.size; i++) { DataForms.DataForm.Option option = list_single_field.options[i]; combobox.append(option.value, option.label); if (option.value == list_single_field.value) combobox.active = i; } combobox.changed.connect(() => { list_single_field.value = combobox.get_active_id(); }); return combobox; case DataForms.DataForm.Type.LIST_MULTI: return null; case DataForms.DataForm.Type.TEXT_PRIVATE: DataForms.DataForm.TextPrivateField text_private_field = field as DataForms.DataForm.TextPrivateField; PasswordEntry entry = new PasswordEntry() { text=text_private_field.value ?? "", valign=Align.CENTER }; entry.changed.connect(() => { text_private_field.value = entry.text; }); return entry; case DataForms.DataForm.Type.TEXT_SINGLE: DataForms.DataForm.TextSingleField text_single_field = field as DataForms.DataForm.TextSingleField; Entry entry = new Entry() { text=text_single_field.value ?? "", valign=Align.CENTER }; entry.changed.connect(() => { text_single_field.value = entry.text; }); return entry; default: return null; } } } dino-0.4.3/main/src/ui/util/helper.vala0000644000000000000000000005404214452563620016412 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.Util { private static Regex URL_REGEX; private static Regex CODE_BLOCK_REGEX; private static Map MATCHING_CHARS; private const unichar[] NON_TRAILING_CHARS = {'\'', '"', ',', '.', ';', '!', '?', '»', '”', '’', '`', '~', '‽', ':', '>', '*', '_'}; private const string[] ALLOWED_SCHEMAS = {"http", "https", "ftp", "ftps", "irc", "ircs", "xmpp", "mailto", "sms", "smsto", "mms", "tel", "geo", "openpgp4fpr", "im", "news", "nntp", "sip", "ssh", "bitcoin", "sftp", "magnet", "vnc"}; private const string[] tango_colors_light = {"FCE94F", "FCAF3E", "E9B96E", "8AE234", "729FCF", "AD7FA8", "EF2929"}; private const string[] tango_colors_medium = {"EDD400", "F57900", "C17D11", "73D216", "3465A4", "75507B", "CC0000"}; private const string[] material_colors_800 = {"D32F2F", "C2185B", "7B1FA2", "512DA8", "303F9F", "1976D2", "0288D1", "0097A7", "00796B", "388E3C", "689F38", "AFB42B", "FFA000", "F57C00", "E64A19", "5D4037"}; private const string[] material_colors_500 = {"F44336", "E91E63", "9C27B0", "673AB7", "3f51B5", "2196F3", "03A9f4", "00BCD4", "009688", "4CAF50", "8BC34a", "CDDC39", "FFC107", "FF9800", "FF5722", "795548"}; private const string[] material_colors_300 = {"E57373", "F06292", "BA68C8", "9575CD", "7986CB", "64B5F6", "4FC3F7", "4DD0E1", "4DB6AC", "81C784", "AED581", "DCE775", "FFD54F", "FFB74D", "FF8A65", "A1887F"}; private const string[] material_colors_200 = {"EF9A9A", "F48FB1", "CE93D8", "B39DDB", "9FA8DA", "90CAF9", "81D4FA", "80DEEA", "80CBC4", "A5D6A7", "C5E1A5", "E6EE9C", "FFE082", "FFCC80", "FFAB91", "BCAAA4"}; public static string get_avatar_hex_color(StreamInteractor stream_interactor, Account account, Jid jid, Conversation? conversation = null) { uint hash = get_relevant_jid(stream_interactor, account, jid, conversation).to_string().hash(); return material_colors_300[hash % material_colors_300.length]; // return tango_colors_light[name.hash() % tango_colors_light.length]; } public static string get_name_hex_color(StreamInteractor stream_interactor, Account account, Jid jid, bool dark_theme = false, Conversation? conversation = null) { uint hash = get_relevant_jid(stream_interactor, account, jid, conversation).to_string().hash(); if (dark_theme) { return material_colors_300[hash % material_colors_300.length]; } else { return material_colors_500[hash % material_colors_500.length]; } // return tango_colors_medium[name.hash() % tango_colors_medium.length]; } private static Jid get_relevant_jid(StreamInteractor stream_interactor, Account account, Jid jid, Conversation? conversation = null) { Conversation conversation_ = conversation ?? stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, account); if (conversation_ != null && conversation_.type_ == Conversation.Type.GROUPCHAT) { Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, account); if (real_jid != null) { return real_jid.bare_jid; } } else { return jid.bare_jid; } return jid; } public static string color_for_show(string show) { switch(show) { case "online": return "#9CCC65"; case "away": return "#FFCA28"; case "chat": return "#66BB6A"; case "xa": return "#EF5350"; case "dnd": return "#EF5350"; default: return "#BDBDBD"; } } public static async AvatarDrawer get_conversation_avatar_drawer(StreamInteractor stream_interactor, Conversation conversation) { return yield get_conversation_participants_avatar_drawer(stream_interactor, conversation, new Jid[0]); } public static async AvatarDrawer get_conversation_participants_avatar_drawer(StreamInteractor stream_interactor, Conversation conversation, owned Jid[] jids) { AvatarManager avatar_manager = stream_interactor.get_module(AvatarManager.IDENTITY); MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); if (conversation.type_ != Conversation.Type.GROUPCHAT) { Jid jid = jids.length == 1 ? jids[0] : conversation.counterpart; Jid avatar_jid = jid; if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) avatar_jid = muc_manager.get_real_jid(avatar_jid, conversation.account) ?? avatar_jid; return new AvatarDrawer().tile(yield avatar_manager.get_avatar(conversation.account, avatar_jid), jids.length == 1 ? get_participant_display_name(stream_interactor, conversation, jid) : get_conversation_display_name(stream_interactor, conversation), Util.get_avatar_hex_color(stream_interactor, conversation.account, jid, conversation)); } if (jids.length > 0) { AvatarDrawer drawer = new AvatarDrawer(); for (int i = 0; i < (jids.length <= 4 ? jids.length : 3); i++) { Jid avatar_jid = jids[i]; Gdk.Pixbuf? part_avatar = yield avatar_manager.get_avatar(conversation.account, avatar_jid); if (part_avatar == null && avatar_jid.equals_bare(conversation.counterpart) && muc_manager.is_private_room(conversation.account, conversation.counterpart)) { avatar_jid = muc_manager.get_real_jid(avatar_jid, conversation.account) ?? avatar_jid; part_avatar = yield avatar_manager.get_avatar(conversation.account, avatar_jid); } drawer.tile(part_avatar, get_participant_display_name(stream_interactor, conversation, jids[i]), Util.get_avatar_hex_color(stream_interactor, conversation.account, jids[i], conversation)); } if (jids.length > 4) { drawer.plus(); } return drawer; } Gdk.Pixbuf? room_avatar = yield avatar_manager.get_avatar(conversation.account, conversation.counterpart); Gee.List? occupants = muc_manager.get_other_offline_members(conversation.counterpart, conversation.account); if (room_avatar != null || !muc_manager.is_private_room(conversation.account, conversation.counterpart) || occupants == null || occupants.size == 0) { return new AvatarDrawer().tile(room_avatar, "#", Util.get_avatar_hex_color(stream_interactor, conversation.account, conversation.counterpart, conversation)); } AvatarDrawer drawer = new AvatarDrawer(); for (int i = 0; i < (occupants.size <= 4 ? occupants.size : 3); i++) { Jid jid = occupants[i]; Jid avatar_jid = jid; Gdk.Pixbuf? part_avatar = yield avatar_manager.get_avatar(conversation.account, avatar_jid); if (part_avatar == null && avatar_jid.equals_bare(conversation.counterpart) && muc_manager.is_private_room(conversation.account, conversation.counterpart)) { avatar_jid = muc_manager.get_real_jid(avatar_jid, conversation.account) ?? avatar_jid; part_avatar = yield avatar_manager.get_avatar(conversation.account, avatar_jid); } drawer.tile(part_avatar, get_participant_display_name(stream_interactor, conversation, jid), Util.get_avatar_hex_color(stream_interactor, conversation.account, jid, conversation)); } if (occupants.size > 4) { drawer.plus(); } return drawer; } public static string get_conversation_display_name(StreamInteractor stream_interactor, Conversation conversation) { return Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s")); } public static string get_participant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid participant, bool me_is_me = false) { return Dino.get_participant_display_name(stream_interactor, conversation, participant, me_is_me ? _("Me") : null); } public static string? get_real_display_name(StreamInteractor stream_interactor, Account account, Jid jid, bool me_is_me = false) { return Dino.get_real_display_name(stream_interactor, account, jid, me_is_me ? _("Me") : null); } public static string get_groupchat_display_name(StreamInteractor stream_interactor, Account account, Jid jid) { return Dino.get_groupchat_display_name(stream_interactor, account, jid); } public static string get_occupant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid jid, bool me_is_me = false, bool muc_real_name = false) { return Dino.get_occupant_display_name(stream_interactor, conversation, jid, me_is_me ? _("Me") : null); } // TODO this has no usages? //public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf, int scale = 0, int width = 0, int height = 0) { // if (scale == 0) scale = image.scale_factor; // Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, image.get_window()); // if (height == 0 && width != 0) { // height = (int) ((double) width / pixbuf.width * pixbuf.height); // } else if (height != 0 && width == 0) { // width = (int) ((double) height / pixbuf.height * pixbuf.width); // } // if (width != 0) { // Cairo.Surface surface_new = new Cairo.Surface.similar_image(surface, Cairo.Format.ARGB32, width, height); // Cairo.Context context = new Cairo.Context(surface_new); // context.scale((double) width * scale / pixbuf.width, (double) height * scale / pixbuf.height); // context.set_source_surface(surface, 0, 0); // context.get_source().set_filter(Cairo.Filter.BEST); // context.paint(); // surface = surface_new; // } // image.set_from_surface(surface); //} public static Gdk.RGBA get_label_pango_color(Label label, string css_color) { Gtk.CssProvider provider = force_color(label, css_color); Gdk.RGBA color_rgba = label.get_style_context().get_color(); label.get_style_context().remove_provider(provider); return color_rgba; } public static string rgba_to_hex(Gdk.RGBA rgba) { return "#%02x%02x%02x%02x".printf( (uint)(Math.round(rgba.red*255)), (uint)(Math.round(rgba.green*255)), (uint)(Math.round(rgba.blue*255)), (uint)(Math.round(rgba.alpha*255))) .up(); } private const string force_background_css = "%s { background-color: %s; }"; private const string force_color_css = "%s { color: %s; }"; public static Gtk.CssProvider force_css(Gtk.Widget widget, string css) { var p = new Gtk.CssProvider(); try { p.load_from_data(css.data); widget.get_style_context().add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } catch (GLib.Error err) { // handle err } return p; } public static void force_background(Gtk.Widget widget, string color, string selector = "*") { force_css(widget, force_background_css.printf(selector, color)); } public static Gtk.CssProvider force_color(Gtk.Widget widget, string color, string selector = "*") { return force_css(widget, force_color_css.printf(selector, color)); } public static void force_error_color(Gtk.Widget widget, string selector = "*") { force_color(widget, "@error_color", selector); } public static bool is_dark_theme(Gtk.Widget widget) { Gdk.RGBA bg = widget.get_style_context().get_color(); return (bg.red > 0.5 && bg.green > 0.5 && bg.blue > 0.5); } private static uint8 is24h = 0; public static bool is_24h_format() { if (is24h == 0) { string p_format = " "; // Leaving room to be filled by strftime Time.local(0).strftime((char[]) p_format.data, "%p"); is24h = p_format.strip() == "" ? 1 : -1; } return is24h == 1; } public static string format_time(DateTime datetime, string format_24h, string format_12h) { string format = Util.is_24h_format() ? format_24h : format_12h; if (!get_charset(null)) { // No UTF-8 support, use simple colon for time instead format = format.replace("∶", ":"); } return datetime.format(format); } public static Regex get_url_regex() { if (URL_REGEX == null) { URL_REGEX = /\b(((http|ftp)s?:\/\/|(ircs?|xmpp|mailto|sms|smsto|mms|tel|geo|openpgp4fpr|im|news|nntp|sip|ssh|bitcoin|sftp|magnet|vnc|urn):)\S+)/; } return URL_REGEX; } public static Map get_matching_chars() { if (MATCHING_CHARS == null) { MATCHING_CHARS = new HashMap(); MATCHING_CHARS[")".get_char(0)] = "(".get_char(0); MATCHING_CHARS["]".get_char(0)] = "[".get_char(0); MATCHING_CHARS["}".get_char(0)] = "{".get_char(0); } return MATCHING_CHARS; } public static string parse_add_markup(string s_, string? highlight_word, bool parse_links, bool parse_text_markup) { bool ignore_out_var = false; return parse_add_markup_theme(s_, highlight_word, parse_links, parse_text_markup, parse_text_markup, false, ref ignore_out_var); } public static string parse_add_markup_theme(string s_, string? highlight_word, bool parse_links, bool parse_text_markup, bool parse_quotes, bool dark_theme, ref bool theme_dependent, bool already_escaped_ = false) { string s = s_; bool already_escaped = already_escaped_; if (parse_quotes) { string gt = already_escaped ? ">" : ">"; Regex quote_regex = new Regex("((?<=\n)" + gt + ".*(\n|$))|(^" + gt + ".*(\n|$))"); MatchInfo quote_match_info; quote_regex.match(s.down(), 0, out quote_match_info); if (quote_match_info.matches()) { int start, end; string dim_color = dark_theme ? "#BDBDBD": "#707070"; theme_dependent = true; quote_match_info.fetch_pos(0, out start, out end); return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, parse_quotes, dark_theme, ref theme_dependent, already_escaped) + @"$gt" + parse_add_markup_theme(s[start + gt.length:end], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, parse_quotes, dark_theme, ref theme_dependent, already_escaped); } } if (parse_links && !already_escaped) { MatchInfo match_info; get_url_regex().match(s.down(), 0, out match_info); while (match_info.matches()) { int start, end; match_info.fetch_pos(0, out start, out end); string link = s[start:end]; if (GLib.Uri.parse_scheme(link) in ALLOWED_SCHEMAS) { Map matching_chars = get_matching_chars(); unichar close_char; int last_char_index = link.length; while (link.get_prev_char(ref last_char_index, out close_char)) { if (matching_chars.has_key(close_char)) { unichar open_char = matching_chars[close_char]; unichar char; int index = 0; int open = 0, close = 0; while (link.get_next_char(ref index, out char)) { if (char == open_char) { open++; } else if (char == close_char) { close++; } } if (close > open) { // Remove last char from url end -= close_char.to_string().length; link = s[start:end]; } else { break; } } else if (close_char in NON_TRAILING_CHARS) { // Remove last char from url end -= close_char.to_string().length; link = s[start:end]; } else { break; } } return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(link, highlight_word, false, false, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } match_info.next(); } } if (!already_escaped) { s = Markup.escape_text(s); already_escaped = true; } if (highlight_word != null) { try { Regex highlight_regex = new Regex("\\b" + Regex.escape_string(highlight_word.down()) + "\\b"); MatchInfo match_info; highlight_regex.match(s.down(), 0, out match_info); if (match_info.matches()) { int start, end; match_info.fetch_pos(0, out start, out end); return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + s[start:end] + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } } catch (RegexError e) { assert_not_reached(); } } if (parse_text_markup) { string[] markup_string = new string[]{"`", "_", "*", "~"}; string[] convenience_tag = new string[]{"tt", "i", "b", "s"}; for (int i = 0; i < markup_string.length; i++) { string markup_esc = Regex.escape_string(markup_string[i]); try { Regex regex = new Regex("(^|\\s)" + markup_esc + "(\\S|\\S.*?\\S)" + markup_esc); MatchInfo match_info; regex.match(s.down(), 0, out match_info); if (match_info.matches()) { int start, end; match_info.fetch_pos(2, out start, out end); return parse_add_markup_theme(s[0:start-1], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + s[start-1:start] + "" + @"<$(convenience_tag[i])>" + s[start:end] + @"" + "" + s[end:end+1] + "" + parse_add_markup_theme(s[end+1:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } } catch (RegexError e) { assert_not_reached(); } } } return s; } /** * This is a heuristic to count emojis in a string {@link http://example.com/} * * @param markup_text string to search in * @return number of emojis, or -1 if text includes non-emojis. */ public int get_only_emoji_count(string markup_text) { int emoji_no = 0; int index_ref = 0; unichar curchar = 0, altchar = 0; bool last_was_emoji = false, last_was_modifier_base = false, last_was_keycap = false; while (markup_text.get_next_char(ref index_ref, out curchar)) { if (last_was_emoji && last_was_keycap && curchar == 0x20E3) { // keycap sequence continue; } last_was_keycap = false; if (last_was_emoji && curchar == 0x200D && markup_text.get_next_char(ref index_ref, out curchar)) { // zero width joiner last_was_emoji = false; emoji_no--; } if (last_was_emoji && curchar == 0xFE0F) { // Variation selector after emoji is useless, ignoring. } else if (last_was_emoji && last_was_modifier_base && ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER)) { // still an emoji, but no longer a modifier base last_was_modifier_base = false; } else if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_PRESENTATION)) { if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER_BASE)) { last_was_modifier_base = true; } emoji_no++; last_was_emoji = true; } else if (curchar == ' ') { last_was_emoji = false; } else if (markup_text.get_next_char(ref index_ref, out altchar) && altchar == 0xFE0F) { // U+FE0F = VARIATION SELECTOR-16 emoji_no++; last_was_emoji = true; last_was_keycap = (curchar >= 0x30 && curchar <= 0x39) || curchar == 0x23 || curchar == 0x2A; } else { return -1; } } return emoji_no; } public string summarize_whitespaces_to_space(string s) { try { return (/\s+/).replace_literal(s, -1, 0, " "); } catch (RegexError e) { critical("RegexError when summarizing whitespaces in '%s': %s", s, e.message); return s; } } public void present_window(Window window) { #if GDK3_WITH_X11 Gdk.X11.Window x11window = window.get_window() as Gdk.X11.Window; if (x11window != null) { window.present_with_time(Gdk.X11.get_server_time(x11window)); } else { window.present(); } #else window.present(); #endif } public bool use_csd() { return ((Application) GLib.Application.get_default()).use_csd(); } public Widget? widget_if_tooltips_active(Widget w) { return use_tooltips() ? w : null; } public string? string_if_tooltips_active(string? s) { return use_tooltips() ? s : null; } public bool use_tooltips() { return Gtk.MINOR_VERSION != 6 || (Gtk.MICRO_VERSION < 4 || Gtk.MICRO_VERSION > 6); } public static void menu_button_set_icon_with_size(MenuButton menu_button, string icon_name, int pixel_size) { #if GTK_4_6 && VALA_0_52 menu_button.set_child(new Image.from_icon_name(icon_name) { pixel_size=pixel_size }); #else menu_button.set_icon_name(icon_name); var button = menu_button.get_first_child() as Button; if (button == null) return; var box = button.child as Box; if (box == null) return; var image = box.get_first_child() as Image; if (image == null) return; image.pixel_size = pixel_size; #endif } } dino-0.4.3/main/src/ui/util/label_hybrid.vala0000644000000000000000000001135214452563620017550 0ustar rootrootusing Gee; using Gtk; namespace Dino.Ui.Util { public class LabelHybrid : Widget { public Stack stack = new Stack(); public Label label = new Label("") { max_width_chars=1, ellipsize=Pango.EllipsizeMode.END }; protected Button button = new Button() { has_frame=false }; internal virtual void init(Widget widget) { this.layout_manager = new BinLayout(); stack.set_parent(this); button.child = label; stack.add_named(button, "label"); stack.add_named(widget, "widget"); button.clicked.connect(() => { show_widget(); }); } public void show_widget() { stack.visible_child_name = "widget"; stack.get_child_by_name("widget").grab_focus(); } public void show_label() { stack.visible_child_name = "label"; } public override void dispose() { stack.unparent(); } } public class EntryLabelHybrid : LabelHybrid { public string text { get { return entry.text; } set { entry.text = value.dup(); update_label(); } } public bool visibility { get { return entry.visibility; } set { entry.visibility = value; } } public float xalign { get { return label.xalign; } set { label.xalign = value; entry.set_alignment(value); } } private Entry? entry_; public Entry entry { get { if (entry_ == null) { entry_ = new Entry(); init(entry_); } return entry_; } set { entry_ = value; } } public EntryLabelHybrid.wrap(Entry e) { init(e); } internal override void init(Widget widget) { Entry? e = widget as Entry; if (e == null) return; entry = e; base.init(entry); update_label(); var key_events = new EventControllerKey(); key_events.key_released.connect(on_key_released); entry.add_controller(key_events); entry.changed.connect(update_label); var focus_events = new EventControllerFocus(); focus_events.leave.connect(update_label); entry.add_controller(focus_events); } private void on_key_released(uint keyval) { if (keyval == Gdk.Key.Return) { show_label(); } } private void on_focus_leave() { show_label(); } private void update_label() { if (visibility) { label.label = entry.text; } else { string filler = ""; for (int i = 0; i < entry.text.length; i++) filler += entry.get_invisible_char().to_string(); label.label = filler; } } } public class ComboBoxTextLabelHybrid : LabelHybrid { public int active { get { return combobox.active; } set { combobox.active = value; } } public float xalign { get { return label.xalign; } set { label.xalign = value; } } private ComboBoxText combobox_; public ComboBoxText combobox { get { if (combobox_ == null) { combobox_ = new ComboBoxText(); init(combobox_); } return combobox_; } set { combobox_ = combobox; } } public ComboBoxTextLabelHybrid.wrap(ComboBoxText cb) { combobox_ = cb; init(cb); } public void append(string id, string text) { combobox.append(id, text); } public string get_active_text() { return combobox.get_active_text(); } internal override void init(Widget widget) { ComboBoxText? combobox = widget as ComboBoxText; if (combobox == null) return; base.init(combobox); update_label(); combobox.changed.connect(() => { update_label(); show_label(); }); button.clicked.connect(() => { combobox.popup(); }); var focus_events = new EventControllerFocus(); focus_events.leave.connect(on_focus_leave); combobox.add_controller(focus_events); } private void on_focus_leave() { update_label(); show_label(); } private void update_label() { label.label = combobox.get_active_text(); } } public class LabelHybridGroup { private Gee.List hybrids = new ArrayList(); public void add(LabelHybrid hybrid) { hybrids.add(hybrid); hybrid.notify["visible-child-name"].connect(() => { if (hybrid.stack.visible_child_name == "label") return; foreach (LabelHybrid h in hybrids) { if (h != hybrid) { h.stack.set_visible_child_name("label"); } } }); } } } dino-0.4.3/main/src/ui/util/preview_file_chooser_native.vala0000644000000000000000000000451514452563620022703 0ustar rootrootusing Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class PreviewFileChooserNative : Object { private const int PREVIEW_SIZE = 180; private const int PREVIEW_PADDING = 5; private Gtk.FileChooserNative? chooser = null; private Image preview_image = new Image(); public PreviewFileChooserNative(string? title, Gtk.Window? parent, FileChooserAction action, string? accept_label, string? cancel_label) { chooser = new FileChooserNative(title, parent, action, accept_label, cancel_label); chooser.set_preview_widget(this.preview_image); chooser.use_preview_label = false; chooser.preview_widget_active = false; chooser.update_preview.connect(on_update_preview); } public void add_filter(owned Gtk.FileFilter filter) { chooser.add_filter(filter); } public SList get_files() { return chooser.get_files(); } public int run() { return chooser.run(); } public string? get_filename() { return chooser.get_filename(); } private void on_update_preview() { Pixbuf preview_pixbuf = get_preview_pixbuf(); if (preview_pixbuf != null) { int extra_space = PREVIEW_SIZE - preview_pixbuf.width; int smaller_half = extra_space/2; int larger_half = extra_space - smaller_half; preview_image.set_margin_start(PREVIEW_PADDING + smaller_half); preview_image.set_margin_end(PREVIEW_PADDING + larger_half); preview_image.set_from_pixbuf(preview_pixbuf); chooser.preview_widget_active = true; } else { chooser.preview_widget_active = false; } } private Pixbuf? get_preview_pixbuf() { string? filename = chooser.get_preview_filename(); if (filename == null) { return null; } int width = 0; int height = 0; Gdk.PixbufFormat? format = Gdk.Pixbuf.get_file_info(filename, out width, out height); if (format == null) { return null; } try { Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file_at_scale(filename, PREVIEW_SIZE, PREVIEW_SIZE, true); pixbuf = pixbuf.apply_embedded_orientation(); return pixbuf; } catch (Error e) { return null; } } } } dino-0.4.3/main/src/ui/util/size_request_box.vala0000644000000000000000000000111414452563620020515 0ustar rootrootusing Gtk; namespace Dino.Ui { public class SizeRequestBox : Box { public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; } public override Gtk.SizeRequestMode get_request_mode() { return size_request_mode; } } public class SizeRequestBin : Widget { public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; } construct { this.layout_manager = new BinLayout(); } public override Gtk.SizeRequestMode get_request_mode() { return size_request_mode; } } } dino-0.4.3/main/src/ui/util/sizing_bin.vala0000644000000000000000000000335714452563620017271 0ustar rootrootusing Gtk; namespace Dino.Ui { public class SizingBin : Widget { public int min_width { get; set; default = -1; } public int target_width { get; set; default = -1; } public int max_width { get; set; default = -1; } public int min_height { get; set; default = -1; } public int target_height { get; set; default = -1; } public int max_height { get; set; default = -1; } construct { layout_manager = new BinLayout(); } public override void size_allocate(int width, int height, int baseline) { if (max_height != -1) height = int.min(height, max_height); if (max_width != -1) width = int.min(width, max_width); base.size_allocate(width, height, baseline); } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { base.measure(orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); if (orientation == Orientation.HORIZONTAL) { if (min_width != -1) minimum = int.max(minimum, min_width); if (max_width != -1) natural = int.min(natural, max_width); if (target_width != -1) natural = int.max(natural, target_width); natural = int.max(natural, minimum); } else { if (min_height != -1) minimum = int.max(minimum, min_height); if (max_height != -1) natural = int.min(natural, max_height); if (target_height != -1) natural = int.max(natural, target_height); natural = int.max(natural, minimum); } } public override void dispose() { var child = this.get_first_child(); if (child != null) child.unparent(); } } } dino-0.4.3/main/src/ui/widgets/0000755000000000000000000000000014452563620014752 5ustar rootrootdino-0.4.3/main/src/ui/widgets/date_separator.vala0000644000000000000000000001025214452563620020614 0ustar rootrootusing Gtk; public class Dino.Ui.ViewModel.DateSeparatorModel : Object { public string date_label { get; set; } } public class Dino.Ui.ViewModel.CompatDateSeparatorModel : DateSeparatorModel { private DateTime date; private uint time_update_timeout = 0; public CompatDateSeparatorModel(DateTime date) { this.date = date; update_time_label(); } private static string get_relative_time(DateTime time) { DateTime time_local = time.to_local(); DateTime now_local = new DateTime.now_local(); if (time_local.get_year() == now_local.get_year() && time_local.get_month() == now_local.get_month() && time_local.get_day_of_month() == now_local.get_day_of_month()) { return _("Today"); } DateTime now_local_minus = now_local.add_days(-1); if (time_local.get_year() == now_local_minus.get_year() && time_local.get_month() == now_local_minus.get_month() && time_local.get_day_of_month() == now_local_minus.get_day_of_month()) { return _("Yesterday"); } if (time_local.get_year() != now_local.get_year()) { return time_local.format("%x"); } TimeSpan timespan = now_local.difference(time_local); if (timespan < 7 * TimeSpan.DAY) { return time_local.format(_("%a, %b %d")); } else { return time_local.format(_("%b %d")); } } private void update_time_label() { date_label = get_relative_time(date); time_update_timeout = set_update_time_label_timeout((int) get_next_time_change(), this); } private static uint set_update_time_label_timeout(int interval, CompatDateSeparatorModel model_) { WeakRef model_weak = WeakRef(model_); return Timeout.add_seconds(interval, () => { CompatDateSeparatorModel? model = (CompatDateSeparatorModel) model_weak.get(); if (model != null && model.time_update_timeout != 0) model.update_time_label(); return false; }); } private int get_next_time_change() { DateTime now = new DateTime.now_local(); return (23 - now.get_hour()) * 3600 + (59 - now.get_minute()) * 60 + (59 - now.get_second()) + 1; } public override void dispose() { base.dispose(); if (time_update_timeout != 0) { Source.remove(time_update_timeout); time_update_timeout = 0; } } } public class Dino.Ui.DateSeparator : Gtk.Widget { public ViewModel.DateSeparatorModel? model { get; set; } public string date_label { get { return label.get_text(); } set { label.set_text(value); } } private Label label = new Label("") { halign = Align.CENTER, hexpand = false }; private Binding? label_text_binding; construct { layout_manager = new BinLayout(); halign = Align.CENTER; hexpand = true; label.add_css_class("dim-label"); label.attributes = new Pango.AttrList(); label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); Box box = new Box(Orientation.HORIZONTAL, 10); box.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }); box.append(label); box.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }); Adw.Clamp clamp = new Adw.Clamp() { maximum_size = 300, tightening_threshold = 300, child = box, halign = Align.CENTER }; clamp.insert_after(this, null); notify["model"].connect(on_model_changed); } private void on_model_changed() { if (label_text_binding != null) label_text_binding.unbind(); if (model != null) { label_text_binding = model.bind_property("date-label", this, "date-label", BindingFlags.SYNC_CREATE); } else { label_text_binding = null; } } public override void dispose() { if (label_text_binding != null) label_text_binding.unbind(); label_text_binding = null; var clamp = get_first_child(); if (clamp != null) { clamp.unparent(); clamp.dispose(); } base.dispose(); } }dino-0.4.3/main/src/ui/widgets/fixed_ratio_picture.vala0000644000000000000000000000643314452563620021655 0ustar rootrootusing Gdk; using Gtk; class Dino.Ui.FixedRatioPicture : Gtk.Widget { public int min_width { get; set; default = -1; } public int max_width { get; set; default = int.MAX; } public int min_height { get; set; default = -1; } public int max_height { get; set; default = int.MAX; } public File file { get { return inner.file; } set { inner.file = value; } } public Gdk.Paintable paintable { get { return inner.paintable; } set { inner.paintable = value; } } #if GTK_4_8 && VALA_0_58 public Gtk.ContentFit content_fit { get { return inner.content_fit; } set { inner.content_fit = value; } } #endif private Gtk.Picture inner = new Gtk.Picture(); construct { set_css_name("picture"); add_css_class("fixed-ratio"); inner.insert_after(this, null); this.notify.connect(queue_resize); } private void measure_target_size(out int width, out int height) { if (width_request != -1 && height_request != -1) { width = width_request; height = height_request; return; } width = min_width; height = min_height; if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.HORIZONTAL, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.VERTICAL, width, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); height = int.max(child_nat, height); } if (height > max_height) { height = max_height; width = min_width; if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.HORIZONTAL, max_height, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); } } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum_baseline = -1; natural_baseline = -1; int width, height; measure_target_size(out width, out height); if (orientation == Orientation.HORIZONTAL) { minimum = min_width; natural = width; } else if (for_size == -1) { minimum = min_height; natural = height; } else { minimum = natural = height * for_size / width; } } public override void size_allocate(int width, int height, int baseline) { if (inner.should_layout()) { inner.allocate(width, height, baseline, null); } } public override SizeRequestMode get_request_mode() { return SizeRequestMode.HEIGHT_FOR_WIDTH; } public override void dispose() { inner.unparent(); base.dispose(); } }dino-0.4.3/main/src/ui/widgets/natural_size_increase.vala0000644000000000000000000000416014452563620022171 0ustar rootrootusing Gtk; public class Dino.Ui.NaturalSizeIncrease : Gtk.Widget { public int min_natural_height { get; set; default = -1; } public int min_natural_width { get; set; default = -1; } construct { this.notify.connect(queue_resize); } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum = 0; if (orientation == Orientation.HORIZONTAL) { natural = min_natural_width; } else { natural = min_natural_height; } natural = int.max(0, natural); minimum_baseline = -1; natural_baseline = -1; Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { int child_min = 0; int child_nat = 0; int child_min_baseline = -1; int child_nat_baseline = -1; child.measure(orientation, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); minimum = int.max(minimum, child_min); natural = int.max(natural, child_nat); if (child_min_baseline > 0) { minimum_baseline = int.max(minimum_baseline, child_min_baseline); } if (child_nat_baseline > 0) { natural_baseline = int.max(natural_baseline, child_nat_baseline); } } child = child.get_next_sibling(); } } public override void size_allocate(int width, int height, int baseline) { Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { child.allocate(width, height, baseline, null); } child = child.get_next_sibling(); } } public override SizeRequestMode get_request_mode() { Widget child = get_first_child(); if (child != null) { return child.get_request_mode(); } return SizeRequestMode.CONSTANT_SIZE; } }dino-0.4.3/main/vapi/0000755000000000000000000000000014452563620013037 5ustar rootrootdino-0.4.3/main/vapi/icu-uc.vapi0000644000000000000000000000050714452563620015107 0ustar rootrootnamespace ICU { [CCode (cprefix = "UCHAR_", cheader_filename = "unicode/uchar.h")] public enum Property { EMOJI, EMOJI_PRESENTATION, EMOJI_MODIFIER, EMOJI_MODIFIER_BASE, } [CCode (cname = "u_hasBinaryProperty", cheader_filename = "unicode/uchar.h")] public bool has_binary_property(unichar c, Property p); } dino-0.4.3/plugins/0000755000000000000000000000000014452563620012635 5ustar rootrootdino-0.4.3/plugins/CMakeLists.txt0000644000000000000000000000035714452563620015402 0ustar rootrootforeach(plugin ${PLUGINS}) if ("omemo" STREQUAL ${plugin}) add_subdirectory(signal-protocol) endif () if ("openpgp" STREQUAL ${plugin}) add_subdirectory(gpgme-vala) endif () add_subdirectory(${plugin}) endforeach(plugin) dino-0.4.3/plugins/gpgme-vala/0000755000000000000000000000000014452563620014655 5ustar rootrootdino-0.4.3/plugins/gpgme-vala/CMakeLists.txt0000644000000000000000000000274014452563620017420 0ustar rootrootfind_package(GPGME REQUIRED) find_packages(GPGME_VALA_PACKAGES REQUIRED Gee GLib GObject ) vala_precompile(GPGME_VALA_C SOURCES "src/gpgme_helper.vala" CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gpgme.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gpgme_public.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gpg-error.vapi" PACKAGES ${GPGME_VALA_PACKAGES} GENERATE_VAPI gpgme-vala GENERATE_HEADER gpgme-vala ) add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/gpgme_fix.h" COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/src/gpgme_fix.h" "${CMAKE_BINARY_DIR}/exports/gpgme_fix.h" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/gpgme_fix.h" COMMENT Copy header file gpgme_fix.h ) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/exports/gpgme.vapi COMMAND cat "${CMAKE_BINARY_DIR}/exports/gpgme-vala.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gpgme_public.vapi" > "${CMAKE_BINARY_DIR}/exports/gpgme.vapi" DEPENDS ${CMAKE_BINARY_DIR}/exports/gpgme-vala.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/gpgme_public.vapi ) add_custom_target(gpgme-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/gpgme_fix.h ${CMAKE_BINARY_DIR}/exports/gpgme.vapi ) set(CFLAGS ${VALA_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/src) add_definitions(${CFLAGS}) add_library(gpgme-vala STATIC ${GPGME_VALA_C} src/gpgme_fix.c) add_dependencies(gpgme-vala gpgme-vapi) target_link_libraries(gpgme-vala ${GPGME_VALA_PACKAGES} gpgme) set_property(TARGET gpgme-vala PROPERTY POSITION_INDEPENDENT_CODE ON) dino-0.4.3/plugins/gpgme-vala/src/0000755000000000000000000000000014452563620015444 5ustar rootrootdino-0.4.3/plugins/gpgme-vala/src/gpgme_fix.c0000644000000000000000000000040114452563620017550 0ustar rootroot#include static GRecMutex gpgme_global_mutex = {0}; gpgme_key_t gpgme_key_ref_vapi (gpgme_key_t key) { gpgme_key_ref(key); return key; } gpgme_key_t gpgme_key_unref_vapi (gpgme_key_t key) { gpgme_key_unref(key); return key; }dino-0.4.3/plugins/gpgme-vala/src/gpgme_fix.h0000644000000000000000000000034014452563620017557 0ustar rootroot#ifndef GPGME_FIX #define GPGME_FIX 1 #include #include static GRecMutex gpgme_global_mutex; gpgme_key_t gpgme_key_ref_vapi (gpgme_key_t key); gpgme_key_t gpgme_key_unref_vapi (gpgme_key_t key); #endifdino-0.4.3/plugins/gpgme-vala/src/gpgme_helper.vala0000644000000000000000000001230514452563620020750 0ustar rootrootusing Gee; using GPG; namespace GPGHelper { private static bool initialized = false; public static string encrypt_armor(string plain, Key[] keys, EncryptFlags flags) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_memory(plain.data, false); Context context = Context.create(); context.set_armor(true); Data enc_data = context.op_encrypt(keys, flags, plain_data); return get_string_from_data(enc_data); } finally { global_mutex.unlock(); } } public static uint8[] encrypt_file(string uri, Key[] keys, EncryptFlags flags, string file_name) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_file(uri); plain_data.set_file_name(file_name); Context context = Context.create(); context.set_armor(true); Data enc_data = context.op_encrypt(keys, flags, plain_data); return get_uint8_from_data(enc_data); } finally { global_mutex.unlock(); } } public static string decrypt(string encr) throws GLib.Error { global_mutex.lock(); try { initialize(); Data enc_data = Data.create_from_memory(encr.data, false); Context context = Context.create(); Data dec_data = context.op_decrypt(enc_data); return get_string_from_data(dec_data); } finally { global_mutex.unlock(); } } public class DecryptedData { public uint8[] data { get; set; } public string filename { get; set; } } public static DecryptedData decrypt_data(uint8[] data) throws GLib.Error { global_mutex.lock(); try { initialize(); Data enc_data = Data.create_from_memory(data, false); Context context = Context.create(); Data dec_data = context.op_decrypt(enc_data); DecryptResult* dec_res = context.op_decrypt_result(); return new DecryptedData() { data=get_uint8_from_data(dec_data), filename=dec_res->file_name}; } finally { global_mutex.unlock(); } } public static string sign(string plain, SigMode mode, Key? key = null) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_memory(plain.data, false); Context context = Context.create(); if (key != null) context.signers_add(key); Data signed_data = context.op_sign(plain_data, mode); return get_string_from_data(signed_data); } finally { global_mutex.unlock(); } } public static string? get_sign_key(string signature, string? text) throws GLib.Error { global_mutex.lock(); try { initialize(); Data sig_data = Data.create_from_memory(signature.data, false); Data text_data; if (text != null) { text_data = Data.create_from_memory(text.data, false); } else { text_data = Data.create(); } Context context = Context.create(); context.op_verify(sig_data, text_data); VerifyResult* verify_res = context.op_verify_result(); if (verify_res == null || verify_res.signatures == null) return null; return verify_res.signatures.fpr; } finally { global_mutex.unlock(); } } public static Gee.List get_keylist(string? pattern = null, bool secret_only = false) throws GLib.Error { global_mutex.lock(); try { initialize(); Gee.List keys = new ArrayList(); Context context = Context.create(); context.op_keylist_start(pattern, secret_only ? 1 : 0); try { while (true) { Key key = context.op_keylist_next(); keys.add(key); } } catch (Error e) { if (e.code != GPGError.ErrorCode.EOF) throw e; } return keys; } finally { global_mutex.unlock(); } } public static Key? get_public_key(string sig) throws GLib.Error { return get_key(sig, false); } public static Key? get_private_key(string sig) throws GLib.Error { return get_key(sig, true); } private static Key? get_key(string sig, bool priv) throws GLib.Error { global_mutex.lock(); try { initialize(); Context context = Context.create(); Key key = context.get_key(sig, priv); return key; } finally { global_mutex.unlock(); } } private static string get_string_from_data(Data data) { const size_t BUF_SIZE = 256; data.seek(0); uint8[] buf = new uint8[BUF_SIZE + 1]; ssize_t len = 0; string res = ""; do { len = data.read(buf, BUF_SIZE); if (len > 0) { buf[len] = 0; res += (string) buf; } } while (len > 0); return res; } private static uint8[] get_uint8_from_data(Data data) { const size_t BUF_SIZE = 256; data.seek(0); uint8[] buf = new uint8[BUF_SIZE + 1]; ssize_t len = 0; ByteArray res = new ByteArray(); do { len = data.read(buf, BUF_SIZE); if (len > 0) { res.append(buf[0:len]); } } while (len > 0); return res.data; } private static void initialize() { if (!initialized) { check_version(); initialized = true; } } } dino-0.4.3/plugins/gpgme-vala/vapi/0000755000000000000000000000000014452563620015614 5ustar rootrootdino-0.4.3/plugins/gpgme-vala/vapi/gpg-error.vapi0000644000000000000000000001636214452563620020411 0ustar rootroot/* gcrypt.vapi * * Copyright: * 2008 Jiqing Qiang * 2008, 2010, 2012-2013 Evan Nemerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jiqing Qiang * Evan Nemerson */ [CCode (cheader_filename = "gpg-error.h")] namespace GPGError { [CCode (cname = "gpg_err_source_t", cprefix = "GPG_ERR_SOURCE_")] public enum ErrorSource { UNKNOWN, GCRYPT, GPG, GPGSM, GPGAGENT, PINENTRY, SCD, GPGME, KEYBOX, KSBA, DIRMNGR, GSTI, ANY, USER_1, USER_2, USER_3, USER_4, /* This is one more than the largest allowed entry. */ DIM } [CCode (cname = "gpg_err_code_t", cprefix = "GPG_ERR_")] public enum ErrorCode { NO_ERROR, GENERAL, UNKNOWN_PACKET, UNKNOWN_VERSION, PUBKEY_ALGO, DIGEST_ALGO, BAD_PUBKEY, BAD_SECKEY, BAD_SIGNATURE, NO_PUBKEY, CHECKSUM, BAD_PASSPHRASE, CIPHER_ALGO, KEYRING_OPEN, INV_PACKET, INV_ARMOR, NO_USER_ID, NO_SECKEY, WRONG_SECKEY, BAD_KEY, COMPR_ALGO, NO_PRIME, NO_ENCODING_METHOD, NO_ENCRYPTION_SCHEME, NO_SIGNATURE_SCHEME, INV_ATTR, NO_VALUE, NOT_FOUND, VALUE_NOT_FOUND, SYNTAX, BAD_MPI, INV_PASSPHRASE, SIG_CLASS, RESOURCE_LIMIT, INV_KEYRING, TRUSTDB, BAD_CERT, INV_USER_ID, UNEXPECTED, TIME_CONFLICT, KEYSERVER, WRONG_PUBKEY_ALGO, TRIBUTE_TO_D_A, WEAK_KEY, INV_KEYLEN, INV_ARG, BAD_URI, INV_URI, NETWORK, UNKNOWN_HOST, SELFTEST_FAILED, NOT_ENCRYPTED, NOT_PROCESSED, UNUSABLE_PUBKEY, UNUSABLE_SECKEY, INV_VALUE, BAD_CERT_CHAIN, MISSING_CERT, NO_DATA, BUG, NOT_SUPPORTED, INV_OP, TIMEOUT, INTERNAL, EOF_GCRYPT, INV_OBJ, TOO_SHORT, TOO_LARGE, NO_OBJ, NOT_IMPLEMENTED, CONFLICT, INV_CIPHER_MODE, INV_FLAG, INV_HANDLE, TRUNCATED, INCOMPLETE_LINE, INV_RESPONSE, NO_AGENT, AGENT, INV_DATA, ASSUAN_SERVER_FAULT, ASSUAN, INV_SESSION_KEY, INV_SEXP, UNSUPPORTED_ALGORITHM, NO_PIN_ENTRY, PIN_ENTRY, BAD_PIN, INV_NAME, BAD_DATA, INV_PARAMETER, WRONG_CARD, NO_DIRMNGR, DIRMNGR, CERT_REVOKED, NO_CRL_KNOWN, CRL_TOO_OLD, LINE_TOO_LONG, NOT_TRUSTED, CANCELED, BAD_CA_CERT, CERT_EXPIRED, CERT_TOO_YOUNG, UNSUPPORTED_CERT, UNKNOWN_SEXP, UNSUPPORTED_PROTECTION, CORRUPTED_PROTECTION, AMBIGUOUS_NAME, CARD, CARD_RESET, CARD_REMOVED, INV_CARD, CARD_NOT_PRESENT, NO_PKCS15_APP, NOT_CONFIRMED, CONFIGURATION, NO_POLICY_MATCH, INV_INDEX, INV_ID, NO_SCDAEMON, SCDAEMON, UNSUPPORTED_PROTOCOL, BAD_PIN_METHOD, CARD_NOT_INITIALIZED, UNSUPPORTED_OPERATION, WRONG_KEY_USAGE, NOTHING_FOUND, WRONG_BLOB_TYPE, MISSING_VALUE, HARDWARE, PIN_BLOCKED, USE_CONDITIONS, PIN_NOT_SYNCED, INV_CRL, BAD_BER, INV_BER, ELEMENT_NOT_FOUND, IDENTIFIER_NOT_FOUND, INV_TAG, INV_LENGTH, INV_KEYINFO, UNEXPECTED_TAG, NOT_DER_ENCODED, NO_CMS_OBJ, INV_CMS_OBJ, UNKNOWN_CMS_OBJ, UNSUPPORTED_CMS_OBJ, UNSUPPORTED_ENCODING, UNSUPPORTED_CMS_VERSION, UNKNOWN_ALGORITHM, INV_ENGINE, PUBKEY_NOT_TRUSTED, DECRYPT_FAILED, KEY_EXPIRED, SIG_EXPIRED, ENCODING_PROBLEM, INV_STATE, DUP_VALUE, MISSING_ACTION, MODULE_NOT_FOUND, INV_OID_STRING, INV_TIME, INV_CRL_OBJ, UNSUPPORTED_CRL_VERSION, INV_CERT_OBJ, UNKNOWN_NAME, LOCALE_PROBLEM, NOT_LOCKED, PROTOCOL_VIOLATION, INV_MAC, INV_REQUEST, UNKNOWN_EXTN, UNKNOWN_CRIT_EXTN, LOCKED, UNKNOWN_OPTION, UNKNOWN_COMMAND, BUFFER_TOO_SHORT, SEXP_INV_LEN_SPEC, SEXP_STRING_TOO_LONG, SEXP_UNMATCHED_PAREN, SEXP_NOT_CANONICAL, SEXP_BAD_CHARACTER, SEXP_BAD_QUOTATION, SEXP_ZERO_PREFIX, SEXP_NESTED_DH, SEXP_UNMATCHED_DH, SEXP_UNEXPECTED_PUNC, SEXP_BAD_HEX_CHAR, SEXP_ODD_HEX_NUMBERS, SEXP_BAD_OCT_CHAR, ASS_GENERAL, ASS_ACCEPT_FAILED, ASS_CONNECT_FAILED, ASS_INV_RESPONSE, ASS_INV_VALUE, ASS_INCOMPLETE_LINE, ASS_LINE_TOO_LONG, ASS_NESTED_COMMANDS, ASS_NO_DATA_CB, ASS_NO_INQUIRE_CB, ASS_NOT_A_SERVER, ASS_NOT_A_CLIENT, ASS_SERVER_START, ASS_READ_ERROR, ASS_WRITE_ERROR, ASS_TOO_MUCH_DATA, ASS_UNEXPECTED_CMD, ASS_UNKNOWN_CMD, ASS_SYNTAX, ASS_CANCELED, ASS_NO_INPUT, ASS_NO_OUTPUT, ASS_PARAMETER, ASS_UNKNOWN_INQUIRE, USER_1, USER_2, USER_3, USER_4, USER_5, USER_6, USER_7, USER_8, USER_9, USER_10, USER_11, USER_12, USER_13, USER_14, USER_15, USER_16, MISSING_ERRNO, UNKNOWN_ERRNO, EOF, E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EAUTH, EBACKGROUND, EBADE, EBADF, EBADFD, EBADMSG, EBADR, EBADRPC, EBADRQC, EBADSLT, EBFONT, EBUSY, ECANCELED, ECHILD, ECHRNG, ECOMM, ECONNABORTED, ECONNREFUSED, ECONNRESET, ED, EDEADLK, EDEADLOCK, EDESTADDRREQ, EDIED, EDOM, EDOTDOT, EDQUOT, EEXIST, EFAULT, EFBIG, EFTYPE, EGRATUITOUS, EGREGIOUS, EHOSTDOWN, EHOSTUNREACH, EIDRM, EIEIO, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, EISNAM, EL2HLT, EL2NSYNC, EL3HLT, EL3RST, ELIBACC, ELIBBAD, ELIBEXEC, ELIBMAX, ELIBSCN, ELNRNG, ELOOP, EMEDIUMTYPE, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, ENAVAIL, ENEEDAUTH, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOANO, ENOBUFS, ENOCSI, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEDIUM, ENOMEM, ENOMSG, ENONET, ENOPKG, ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTBLK, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTNAM, ENOTSOCK, ENOTSUP, ENOTTY, ENOTUNIQ, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPFNOSUPPORT, EPIPE, EPROCLIM, EPROCUNAVAIL, EPROGMISMATCH, EPROGUNAVAIL, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EREMCHG, EREMOTE, EREMOTEIO, ERESTART, EROFS, ERPCMISMATCH, ESHUTDOWN, ESOCKTNOSUPPORT, ESPIPE, ESRCH, ESRMNT, ESTALE, ESTRPIPE, ETIME, ETIMEDOUT, ETOOMANYREFS, ETXTBSY, EUCLEAN, EUNATCH, EUSERS, EWOULDBLOCK, EXDEV, EXFULL, /* This is one more than the largest allowed entry. */ CODE_DIM } [CCode (cname = "gpg_err_code_t", cprefix = "gpg_err_")] public struct Error : uint { [CCode (cname = "gpg_err_make")] public Error (ErrorSource source, ErrorCode code); [CCode (cname = "gpg_err_make_from_errno")] public Error.from_errno (ErrorSource source, int err); public ErrorCode code { [CCode (cname = "gpg_err_code")] get; } public ErrorSource source { [CCode (cname = "gpg_err_source")] get; } [CCode (cname = "gpg_strerror")] public unowned string to_string (); [CCode (cname = "gpg_strsource")] public unowned string source_to_string (); } }dino-0.4.3/plugins/gpgme-vala/vapi/gpgme.deps0000644000000000000000000000001214452563620017561 0ustar rootrootgpg-error dino-0.4.3/plugins/gpgme-vala/vapi/gpgme.vapi0000644000000000000000000003607714452563620017611 0ustar rootroot/* libgpgme.vapi * * Copyright (C) 2009 Sebastian Reichel * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ [CCode (lower_case_cprefix = "gpgme_", cheader_filename = "gpgme.h,gpgme_fix.h")] namespace GPG { public static GLib.RecMutex global_mutex; [CCode (cname = "struct _gpgme_engine_info")] public struct EngineInfo { EngineInfo* next; Protocol protocol; string file_name; string version; string req_version; string? home_dir; } [CCode (cname = "struct _gpgme_op_verify_result")] public struct VerifyResult { Signature* signatures; string? file_name; } [CCode (cname = "struct _gpgme_op_sign_result")] public struct SignResult { InvalidKey invalid_signers; Signature* signatures; } [CCode (cname = "struct _gpgme_op_encrypt_result")] public struct EncryptResult { InvalidKey invalid_signers; } [CCode (cname = "struct _gpgme_op_decrypt_result")] public struct DecryptResult { string unsupported_algorithm; bool wrong_key_usage; Recipient recipients; string file_name; } [CCode (cname = "struct _gpgme_recipient")] public struct Recipient { Recipient *next; string keyid; PublicKeyAlgorithm pubkey_algo; GPGError.Error status; } [CCode (cname = "struct _gpgme_invalid_key")] public struct InvalidKey { InvalidKey *next; string fpr; GPGError.Error reason; } [CCode (cname = "struct _gpgme_signature")] public struct Signature { Signature *next; Sigsum summary; string fpr; GPGError.Error status; SigNotation notations; ulong timestamp; ulong exp_timestamp; bool wrong_key_usage; PKAStatus pka_trust; bool chain_model; Validity validity; GPGError.Error validity_reason; PublicKeyAlgorithm pubkey_algo; HashAlgorithm hash_algo; string? pka_adress; } public enum PKAStatus { NOT_AVAILABLE, BAD, OKAY, RFU } [CCode (cname = "gpgme_sigsum_t", cprefix = "GPGME_SIGSUM_")] public enum Sigsum { VALID, GREEN, RED, KEY_REVOKED, KEY_EXPIRED, SIG_EXPIRED, KEY_MISSING, CRL_MISSING, CRL_TOO_OLD, BAD_POLICY, SYS_ERROR } [CCode (cname = "gpgme_data_encoding_t", cprefix = "GPGME_DATA_ENCODING_")] public enum DataEncoding { NONE, BINARY, BASE64, ARMOR, URL, URLESC, URL0 } [CCode (cname = "gpgme_hash_algo_t", cprefix = "GPGME_MD_")] public enum HashAlgorithm { NONE, MD5, SHA1, RMD160, MD2, TIGER, HAVAL, SHA256, SHA384, SHA512, MD4, MD_CRC32, MD_CRC32_RFC1510, MD_CRC24_RFC2440 } [CCode (cname = "gpgme_export_mode_t", cprefix = "GPGME_EXPORT_MODE_")] public enum ExportMode { EXTERN } [CCode (cprefix = "GPGME_AUDITLOG_")] public enum AuditLogFlag { HTML, WITH_HELP } [CCode (cname = "gpgme_status_code_t", cprefix = "GPGME_STATUS_")] public enum StatusCode { EOF, ENTER, LEAVE, ABORT, GOODSIG, BADSIG, ERRSIG, BADARMOR, RSA_OR_IDEA, KEYEXPIRED, KEYREVOKED, TRUST_UNDEFINED, TRUST_NEVER, TRUST_MARGINAL, TRUST_FULLY, TRUST_ULTIMATE, SHM_INFO, SHM_GET, SHM_GET_BOOL, SHM_GET_HIDDEN, NEED_PASSPHRASE, VALIDSIG, SIG_ID, SIG_TO, ENC_TO, NODATA, BAD_PASSPHRASE, NO_PUBKEY, NO_SECKEY, NEED_PASSPHRASE_SYM, DECRYPTION_FAILED, DECRYPTION_OKAY, MISSING_PASSPHRASE, GOOD_PASSPHRASE, GOODMDC, BADMDC, ERRMDC, IMPORTED, IMPORT_OK, IMPORT_PROBLEM, IMPORT_RES, FILE_START, FILE_DONE, FILE_ERROR, BEGIN_DECRYPTION, END_DECRYPTION, BEGIN_ENCRYPTION, END_ENCRYPTION, DELETE_PROBLEM, GET_BOOL, GET_LINE, GET_HIDDEN, GOT_IT, PROGRESS, SIG_CREATED, SESSION_KEY, NOTATION_NAME, NOTATION_DATA, POLICY_URL, BEGIN_STREAM, END_STREAM, KEY_CREATED, USERID_HINT, UNEXPECTED, INV_RECP, NO_RECP, ALREADY_SIGNED, SIGEXPIRED, EXPSIG, EXPKEYSIG, TRUNCATED, ERROR, NEWSIG, REVKEYSIG, SIG_SUBPACKET, NEED_PASSPHRASE_PIN, SC_OP_FAILURE, SC_OP_SUCCESS, CARDCTRL, BACKUP_KEY_CREATED, PKA_TRUST_BAD, PKA_TRUST_GOOD, PLAINTEXT } [Flags] [CCode (cname="unsigned int")] public enum ImportStatusFlags { [CCode (cname = "GPGME_IMPORT_NEW")] NEW, [CCode (cname = "GPGME_IMPORT_UID")] UID, [CCode (cname = "GPGME_IMPORT_SIG")] SIG, [CCode (cname = "GPGME_IMPORT_SUBKEY")] SUBKEY, [CCode (cname = "GPGME_IMPORT_SECRET")] SECRET } [Compact] [CCode (cname = "struct gpgme_context", free_function = "gpgme_release", cprefix = "gpgme_")] public class Context { private static GPGError.Error new(out Context ctx); public static Context create() throws GLib.Error { Context ctx; throw_if_error(@new(out ctx)); return ctx; } public GPGError.Error set_protocol(Protocol p); public Protocol get_protocol(); public void set_armor(bool yes); public bool get_armor(); public void set_textmode(bool yes); public bool get_textmode(); public GPGError.Error set_keylist_mode(KeylistMode mode); public KeylistMode get_keylist_mode(); public void set_include_certs(int nr_of_certs = -256); public int get_include_certs(); public void set_passphrase_cb(passphrase_callback cb, void* hook_value = null); public void get_passphrase_cb(out passphrase_callback cb, out void* hook_value); public GPGError.Error set_locale(int category, string val); [CCode (cname = "gpgme_ctx_get_engine_info")] public EngineInfo* get_engine_info(); [CCode (cname = "gpgme_ctx_set_engine_info")] public GPGError.Error set_engine_info(Protocol proto, string file_name, string home_dir); public void signers_clear(); public GPGError.Error signers_add(Key key); public Key* signers_enum(int n); public void sig_notation_clear(); public GPGError.Error sig_notation_add(string name, string val, SigNotationFlags flags); public SigNotation* sig_notation_get(); [CCode (cname = "gpgme_get_key")] private GPGError.Error get_key_(string fpr, out Key key, bool secret); [CCode (cname = "gpgme_get_key_")] public Key get_key(string fpr, bool secret) throws GLib.Error { Key key; throw_if_error(get_key_(fpr, out key, secret)); return key; } public Context* wait(out GPGError.Error status, bool hang); public SignResult* op_sign_result(); [CCode (cname = "gpgme_op_sign")] public GPGError.Error op_sign_(Data plain, Data sig, SigMode mode); [CCode (cname = "gpgme_op_sign_")] public Data op_sign(Data plain, SigMode mode) throws GLib.Error { Data sig = Data.create(); throw_if_error(op_sign_(plain, sig, mode)); return sig; } public VerifyResult* op_verify_result(); [CCode (cname = "gpgme_op_verify")] public GPGError.Error op_verify_(Data sig, Data signed_text, Data? plaintext); [CCode (cname = "gpgme_op_verify_")] public Data op_verify(Data sig, Data signed_text) throws GLib.Error { Data plaintext = Data.create(); throw_if_error(op_verify_(sig, signed_text, plaintext)); return plaintext; } public EncryptResult* op_encrypt_result(); [CCode (cname = "gpgme_op_encrypt")] public GPGError.Error op_encrypt_([CCode (array_length = false)] Key[] recp, EncryptFlags flags, Data plain, Data cipher); [CCode (cname = "gpgme_op_encrypt_")] public Data op_encrypt(Key[] recp, EncryptFlags flags, Data plain) throws GLib.Error { Data cipher = Data.create(); throw_if_error(op_encrypt_(recp, flags, plain, cipher)); return cipher; } public DecryptResult* op_decrypt_result(); [CCode (cname = "gpgme_op_decrypt")] public GPGError.Error op_decrypt_(Data cipher, Data plain); [CCode (cname = "gpgme_op_decrypt_")] public Data op_decrypt(Data cipher) throws GLib.Error { Data plain = Data.create(); throw_if_error(op_decrypt_(cipher, plain)); return plain; } public GPGError.Error op_export(string? pattern, ExportMode mode, Data keydata); public GPGError.Error op_import(Data keydata); public unowned ImportResult op_import_result(); [CCode (cname = "gpgme_op_keylist_start")] private GPGError.Error op_keylist_start_(string? pattern = null, int secret_only = 0); [CCode (cname = "gpgme_op_keylist_start_")] public void op_keylist_start(string? pattern = null, int secret_only = 0) throws GLib.Error { throw_if_error(op_keylist_start_(pattern, secret_only)); } [CCode (cname = "gpgme_op_keylist_next")] private GPGError.Error op_keylist_next_(out Key key); [CCode (cname = "gpgme_op_keylist_next_")] public Key op_keylist_next() throws GLib.Error { Key key; throw_if_error(op_keylist_next_(out key)); return key; } [CCode (cname = "gpgme_op_keylist_end")] private GPGError.Error op_keylist_end_(); [CCode (cname = "gpgme_op_keylist_end_")] public void op_keylist_end() throws GLib.Error { throw_if_error(op_keylist_end_()); } public KeylistResult op_keylist_result(); } [Compact] [CCode (cname = "struct _gpgme_import_status")] public class ImportStatus { public ImportStatus? next; public string fpr; public GPGError.Error result; public ImportStatusFlags status; } [Compact] [CCode (cname = "struct _gpgme_op_import_result")] public class ImportResult { public int considered; public int no_user_id; public int imported; public int imported_rsa; public int unchanged; public int new_user_ids; public int new_sub_keys; public int new_signatures; public int new_revocations; public int secret_read; public int secret_imported; public int secret_unchanged; public int not_imported; public ImportStatus imports; } [Compact] [CCode (cname = "struct _gpgme_op_keylist_result")] public class KeylistResult { uint truncated; } [Compact] [CCode (cname = "struct gpgme_data", free_function = "gpgme_data_release", cprefix = "gpgme_data_")] public class Data { public static GPGError.Error new(out Data d); public static Data create() throws GLib.Error { Data data; throw_if_error(@new(out data)); return data; } [CCode (cname = "gpgme_data_new_from_mem")] public static GPGError.Error new_from_memory(out Data d, char[] buffer, bool copy); public static Data create_from_memory(uint8[] buffer, bool copy) throws GLib.Error { Data data; throw_if_error(new_from_memory(out data, (char[]) buffer, copy)); return data; } [CCode (cname = "gpgme_data_new_from_file")] public static GPGError.Error new_from_file(out Data d, string filename, int copy = 1); public static Data create_from_file(string filename, int copy = 1) throws GLib.Error { Data data; throw_if_error(new_from_file(out data, filename, copy)); return data; } [CCode (cname = "gpgme_data_release_and_get_mem")] public string release_and_get_mem(out size_t len); public ssize_t read([CCode (array_length = false)] uint8[] buf, size_t len); public ssize_t write(uint8[] buf); public long seek(long offset, int whence=0); public GPGError.Error set_file_name(string file_name); public DataEncoding* get_encoding(); public GPGError.Error set_encoding(DataEncoding enc); } [CCode (cname = "gpgme_get_protocol_name")] public unowned string get_protocol_name(Protocol p); [CCode (cname = "gpgme_pubkey_algo_name")] public unowned string get_public_key_algorithm_name(PublicKeyAlgorithm algo); [CCode (cname = "gpgme_hash_algo_name")] public unowned string get_hash_algorithm_name(HashAlgorithm algo); [CCode (cname = "gpgme_passphrase_cb_t", has_target = false)] public delegate GPGError.Error passphrase_callback(void* hook, string uid_hint, string passphrase_info, bool prev_was_bad, int fd); [CCode (cname = "gpgme_engine_check_version")] public GPGError.Error engine_check_version(Protocol proto); [CCode (cname = "gpgme_get_engine_information")] public GPGError.Error get_engine_information(out EngineInfo engine_info); [CCode (cname = "gpgme_strerror_r")] public int strerror_r(GPGError.Error err, uint8[] buf); [CCode (cname = "gpgme_strerror")] public unowned string strerror(GPGError.Error err); private void throw_if_error(GPGError.Error error) throws GLib.Error { if (error.code != GPGError.ErrorCode.NO_ERROR) { throw new GLib.Error(-1, error.code, "%s", error.to_string()); } } } dino-0.4.3/plugins/gpgme-vala/vapi/gpgme_public.vapi0000644000000000000000000000666514452563620021147 0ustar rootroot[CCode (lower_case_cprefix = "gpgme_", cheader_filename = "gpgme.h,gpgme_fix.h")] namespace GPG { [CCode (cname = "gpgme_check_version")] public unowned string check_version(string? required_version = null); [Compact] [CCode (cname = "struct _gpgme_key", ref_function = "gpgme_key_ref_vapi", unref_function = "gpgme_key_unref_vapi", free_function = "gpgme_key_release")] public class Key { public bool revoked; public bool expired; public bool disabled; public bool invalid; public bool can_encrypt; public bool can_sign; public bool can_certify; public bool can_authenticate; public bool is_qualified; public bool secret; public Protocol protocol; public string issuer_serial; public string issuer_name; public string chain_id; public Validity owner_trust; [CCode(array_null_terminated = true)] public SubKey[] subkeys; [CCode(array_null_terminated = true)] public UserID[] uids; public KeylistMode keylist_mode; // public string fpr; // requires gpgme >= 1.7.0 public string fpr { get { return subkeys[0].fpr; } } } [CCode (cname = "struct _gpgme_user_id")] public struct UserID { UserID* next; bool revoked; bool invalid; Validity validity; string uid; string name; string email; string comment; KeySig signatures; } [CCode (cname = "struct _gpgme_key_sig")] public struct KeySig { KeySig* next; bool invoked; bool expired; bool invalid; bool exportable; PublicKeyAlgorithm algo; string keyid; long timestamp; long expires; // GPGError.Error status; string uid; string name; string email; string comment; uint sig_class; SigNotation notations; } [CCode (cname = "struct _gpgme_subkey")] public struct SubKey { SubKey* next; bool revoked; bool expired; bool disabled; bool invalid; bool can_encrypt; bool can_sign; bool can_certify; bool secret; bool can_authenticate; bool is_qualified; bool is_cardkey; PublicKeyAlgorithm algo; uint length; string keyid; string fpr; long timestamp; long expires; string? cardnumber; } [CCode (cname = "struct _gpgme_sig_notation")] public struct SigNotation { SigNotation* next; string? name; string value; int name_len; int value_len; SigNotationFlags flags; bool human_readable; bool critical; } [CCode (cname = "gpgme_sig_notation_flags_t", cprefix = "GPGME_SIG_NOTATION_")] public enum SigNotationFlags { HUMAN_READABLE, CRITICAL } [CCode (cname = "gpgme_sig_mode_t", cprefix = "GPGME_SIG_MODE_")] public enum SigMode { NORMAL, DETACH, CLEAR } [CCode (cname = "gpgme_encrypt_flags_t", cprefix = "GPGME_ENCRYPT_")] public enum EncryptFlags { ALWAYS_TRUST, NO_ENCRYPT_TO } [CCode (cname = "gpgme_pubkey_algo_t", cprefix = "GPGME_PK_")] public enum PublicKeyAlgorithm { RSA, RSA_E, RSA_S, ELG_E, DSA, ELG } [CCode (cname = "gpgme_protocol_t", cprefix = "GPGME_PROTOCOL_")] public enum Protocol { OpenPGP, CMS, GPGCONF, ASSUAN, UNKNOWN } [CCode (cname = "gpgme_keylist_mode_t", cprefix = "GPGME_KEYLIST_MODE_")] public enum KeylistMode { LOCAL, EXTERN, SIGS, SIG_NOTATIONS, EPHEMERAL, VALIDATE } [CCode (cname = "gpgme_validity_t", cprefix = "GPGME_VALIDITY_")] public enum Validity { UNKNOWN, UNDEFINED, NEVER, MARGINAL, FULL, ULTIMATE } }dino-0.4.3/plugins/http-files/0000755000000000000000000000000014452563620014714 5ustar rootrootdino-0.4.3/plugins/http-files/CMakeLists.txt0000644000000000000000000000175014452563620017457 0ustar rootrootinclude(SoupVersion) find_packages(HTTP_FILES_PACKAGES REQUIRED Gee GLib GModule GObject GTK4 ${Soup} ) set(HTTP_FILES_DEFINITIONS) if(${Soup}_VERSION VERSION_GREATER_EQUAL "3.0") set(HTTP_FILES_DEFINITIONS ${HTTP_FILES_DEFINITIONS} SOUP_3_0) endif() vala_precompile(HTTP_FILES_VALA_C SOURCES src/file_provider.vala src/file_sender.vala src/plugin.vala src/register_plugin.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi PACKAGES ${HTTP_FILES_PACKAGES} DEFINITIONS ${HTTP_FILES_DEFINITIONS} ) add_definitions(${VALA_CFLAGS}) add_library(http-files SHARED ${HTTP_FILES_VALA_C}) target_link_libraries(http-files libdino ${HTTP_FILES_PACKAGES}) set_target_properties(http-files PROPERTIES PREFIX "") set_target_properties(http-files PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS http-files ${PLUGIN_INSTALL}) dino-0.4.3/plugins/http-files/src/0000755000000000000000000000000014452563620015503 5ustar rootrootdino-0.4.3/plugins/http-files/src/file_provider.vala0000644000000000000000000002131214452563620021200 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Plugins.HttpFiles { public class FileProvider : Dino.FileProvider, Object { private StreamInteractor stream_interactor; private Dino.Database dino_db; private Soup.Session session; private static Regex http_url_regex = /^https?:\/\/([^\s#]*)$/; // Spaces are invalid in URLs and we can't use fragments for downloads private static Regex omemo_url_regex = /^aesgcm:\/\/(.*)#(([A-Fa-f0-9]{2}){48}|([A-Fa-f0-9]{2}){44})$/; public FileProvider(StreamInteractor stream_interactor, Dino.Database dino_db) { this.stream_interactor = stream_interactor; this.dino_db = dino_db; this.session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this)); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "MESSAGE_REINTERPRETING"; } } public override string[] after_actions { get { return after_actions_const; } } private FileProvider outer; private StreamInteractor stream_interactor; public ReceivedMessageListener(FileProvider outer) { this.outer = outer; this.stream_interactor = outer.stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { string? oob_url = Xmpp.Xep.OutOfBandData.get_url_from_message(stanza); bool normal_file = oob_url != null && oob_url == message.body && FileProvider.http_url_regex.match(message.body); bool omemo_file = FileProvider.omemo_url_regex.match(message.body); if (normal_file || omemo_file) { outer.on_file_message(message, conversation); return true; } return false; } } private class LimitInputStream : InputStream, PollableInputStream { InputStream inner; int64 remaining_size; public LimitInputStream(InputStream inner, int64 max_size) { this.inner = inner; this.remaining_size = max_size; } public bool can_poll() { return inner is PollableInputStream && ((PollableInputStream)inner).can_poll(); } public PollableSource create_source(Cancellable? cancellable = null) { if (!can_poll()) throw new IOError.NOT_SUPPORTED("Stream is not pollable"); return ((PollableInputStream)inner).create_source(cancellable); } public bool is_readable() { if (!can_poll()) throw new IOError.NOT_SUPPORTED("Stream is not pollable"); return remaining_size <= 0 || ((PollableInputStream)inner).is_readable(); } private ssize_t check_limit(ssize_t read) throws IOError { this.remaining_size -= read; if (remaining_size < 0) throw new IOError.FAILED("Stream length exceeded limit"); return read; } public override ssize_t read(uint8[] buffer, Cancellable? cancellable = null) throws IOError { return check_limit(inner.read(buffer, cancellable)); } public override async ssize_t read_async(uint8[]? buffer, int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { return check_limit(yield inner.read_async(buffer, io_priority, cancellable)); } public ssize_t read_nonblocking_fn(uint8[] buffer) throws Error { if (!is_readable()) throw new IOError.WOULD_BLOCK("Stream is not readable"); return read(buffer); } public override bool close(Cancellable? cancellable = null) throws IOError { return inner.close(cancellable); } public override async bool close_async(int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { return yield inner.close_async(io_priority, cancellable); } } private void on_file_message(Entities.Message message, Conversation conversation) { var additional_info = message.id.to_string(); var receive_data = new HttpFileReceiveData(); receive_data.url = message.body; var file_meta = new HttpFileMeta(); file_meta.file_name = extract_file_name_from_url(message.body); file_meta.message = message; file_incoming(additional_info, message.from, message.time, message.local_time, conversation, receive_data, file_meta); } public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) return file_meta; var head_message = new Soup.Message("HEAD", http_receive_data.url); head_message.request_headers.append("Accept-Encoding", "identity"); try { #if SOUP_3_0 yield session.send_async(head_message, GLib.Priority.LOW, null); #else yield session.send_async(head_message, null); #endif } catch (Error e) { throw new FileReceiveError.GET_METADATA_FAILED("HEAD request failed"); } string? content_type = null, content_length = null; head_message.response_headers.foreach((name, val) => { if (name.down() == "content-type") content_type = val; if (name.down() == "content-length") content_length = val; }); file_meta.mime_type = content_type; if (content_length != null) { file_meta.size = int64.parse(content_length); } return file_meta; } public Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { return Encryption.NONE; } public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) assert(false); var get_message = new Soup.Message("GET", http_receive_data.url); try { #if SOUP_3_0 InputStream stream = yield session.send_async(get_message, GLib.Priority.LOW, file_transfer.cancellable); #else InputStream stream = yield session.send_async(get_message, file_transfer.cancellable); #endif if (file_meta.size != -1) { return new LimitInputStream(stream, file_meta.size); } else { return stream; } } catch (Error e) { throw new FileReceiveError.DOWNLOAD_FAILED("Downloading file error: %s".printf(e.message)); } } public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation"); Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); if (message == null) throw new FileReceiveError.GET_METADATA_FAILED("No message"); var file_meta = new HttpFileMeta(); file_meta.size = file_transfer.size; file_meta.mime_type = file_transfer.mime_type; file_meta.file_name = extract_file_name_from_url(message.body); file_meta.message = message; return file_meta; } public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); if (conversation == null) return null; Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); if (message == null) return null; var receive_data = new HttpFileReceiveData(); receive_data.url = message.body; return receive_data; } private string extract_file_name_from_url(string url) { string ret = url; if (ret.contains("#")) { ret = ret.substring(0, ret.last_index_of("#")); } ret = Uri.unescape_string(ret.substring(ret.last_index_of("/") + 1)); return ret; } public int get_id() { return 0; } } } dino-0.4.3/plugins/http-files/src/file_sender.vala0000644000000000000000000001406214452563620020632 0ustar rootrootusing Dino.Entities; using Xmpp; using Gee; namespace Dino.Plugins.HttpFiles { public class HttpFileSender : FileSender, Object { private StreamInteractor stream_interactor; private Database db; private Soup.Session session; private HashMap max_file_sizes = new HashMap(Account.hash_func, Account.equals_func); public HttpFileSender(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(check_add_oob); } public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError { HttpFileSendData send_data = new HttpFileSendData(); if (send_data == null) return null; Xmpp.XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) return null; try { var slot_result = yield stream_interactor.module_manager.get_module(file_transfer.account, Xmpp.Xep.HttpFileUpload.Module.IDENTITY).request_slot(stream, file_transfer.server_file_name, file_meta.size, file_meta.mime_type); send_data.url_down = slot_result.url_get; send_data.url_up = slot_result.url_put; send_data.headers = slot_result.headers; } catch (Xep.HttpFileUpload.HttpFileTransferError e) { throw new FileSendError.UPLOAD_FAILED("Http file upload XMPP error: %s".printf(e.message)); } return send_data; } public async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return; yield upload(file_transfer, send_data, file_meta); Entities.Message message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(send_data.url_down, conversation); file_transfer.info = message.id.to_string(); message.encryption = send_data.encrypt_message ? conversation.encryption : Encryption.NONE; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(message, conversation); } public async bool can_send(Conversation conversation, FileTransfer file_transfer) { if (!max_file_sizes.has_key(conversation.account)) return false; return file_transfer.size < max_file_sizes[conversation.account]; } public async long get_file_size_limit(Conversation conversation) { long? max_size = max_file_sizes[conversation.account]; if (max_size != null) { return max_size; } return -1; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { return false; } public async bool is_upload_available(Conversation conversation) { lock (max_file_sizes) { return max_file_sizes.has_key(conversation.account); } } #if !SOUP_3_0 private static void transfer_more_bytes(InputStream stream, Soup.MessageBody body) { uint8[] bytes = new uint8[4096]; ssize_t read = stream.read(bytes); if (read == 0) { body.complete(); return; } bytes.length = (int)read; body.append_buffer(new Soup.Buffer.take(bytes)); } #endif private async void upload(FileTransfer file_transfer, HttpFileSendData file_send_data, FileMeta file_meta) throws FileSendError { Xmpp.XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) return; var put_message = new Soup.Message("PUT", file_send_data.url_up); #if SOUP_3_0 put_message.set_request_body(file_meta.mime_type, file_transfer.input_stream, (ssize_t) file_meta.size); #else put_message.request_headers.set_content_type(file_meta.mime_type, null); put_message.request_headers.set_content_length(file_meta.size); put_message.request_body.set_accumulate(false); put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); #endif foreach (var entry in file_send_data.headers.entries) { put_message.request_headers.append(entry.key, entry.value); } try { #if SOUP_3_0 yield session.send_async(put_message, GLib.Priority.LOW, file_transfer.cancellable); #else yield session.send_async(put_message, file_transfer.cancellable); #endif if (put_message.status_code < 200 || put_message.status_code >= 300) { throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(put_message.status_code.to_string())); } } catch (Error e) { throw new FileSendError.UPLOAD_FAILED("HTTP upload error: %s".printf(e.message)); } } private void on_stream_negotiated(Account account, XmppStream stream) { stream_interactor.module_manager.get_module(account, Xmpp.Xep.HttpFileUpload.Module.IDENTITY).feature_available.connect((stream, max_file_size) => { lock (max_file_sizes) { max_file_sizes[account] = max_file_size; } upload_available(account); }); } private void check_add_oob(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (message.encryption == Encryption.NONE && message.body.has_prefix("http") && message_is_file(db, message)) { Xep.OutOfBandData.add_url_to_message(message_stanza, message_stanza.body); } } public int get_id() { return 0; } public float get_priority() { return 100; } } } dino-0.4.3/plugins/http-files/src/plugin.vala0000644000000000000000000000173314452563620017652 0ustar rootrootextern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.HttpFiles { public class Plugin : RootInterface, Object { public Dino.Application app; public FileProvider file_provider; public FileSender file_sender; public void registered(Dino.Application app) { this.app = app; file_provider = new FileProvider(app.stream_interactor, app.db); file_sender = new HttpFileSender(app.stream_interactor, app.db); app.stream_interactor.get_module(FileManager.IDENTITY).add_provider(file_provider); app.stream_interactor.get_module(FileManager.IDENTITY).add_sender(file_sender); } public void shutdown() { // Nothing to do } } private bool message_is_file(Database db, Entities.Message message) { Qlite.QueryBuilder builder = db.file_transfer.select({db.file_transfer.id}).with(db.file_transfer.info, "=", message.id.to_string()); return builder.count() > 0; } } dino-0.4.3/plugins/http-files/src/register_plugin.vala0000644000000000000000000000014214452563620021547 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.HttpFiles.Plugin); } dino-0.4.3/plugins/ice/0000755000000000000000000000000014452563620013375 5ustar rootrootdino-0.4.3/plugins/ice/CMakeLists.txt0000644000000000000000000000176714452563620016150 0ustar rootrootfind_package(Nice 0.1.15 REQUIRED) find_package(GnuTLS REQUIRED) find_packages(ICE_PACKAGES REQUIRED Gee GLib GModule GObject GIO GDKPixbuf2 ) vala_precompile(ICE_VALA_C SOURCES src/dtls_srtp.vala src/module.vala src/plugin.vala src/transport_parameters.vala src/util.vala src/register_plugin.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/nice.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/gnutls.vapi PACKAGES ${ICE_PACKAGES} ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="ice") add_library(ice SHARED ${ICE_VALA_C}) target_link_libraries(ice libdino crypto-vala ${ICE_PACKAGES} nice gnutls) set_target_properties(ice PROPERTIES PREFIX "") set_target_properties(ice PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS ice ${PLUGIN_INSTALL}) dino-0.4.3/plugins/ice/src/0000755000000000000000000000000014452563620014164 5ustar rootrootdino-0.4.3/plugins/ice/src/dtls_srtp.vala0000644000000000000000000003150314452563620017051 0ustar rootrootusing GnuTLS; namespace Dino.Plugins.Ice.DtlsSrtp { public class CredentialsCapsule { public uint8[] own_fingerprint; public X509.Certificate[] own_cert; public X509.PrivateKey private_key; } public class Handler { public signal void send_data(uint8[] data); public bool ready { get { return srtp_session.has_encrypt && srtp_session.has_decrypt; }} public Mode mode { get; set; default = Mode.CLIENT; } public uint8[] own_fingerprint { get; private set; } public uint8[] peer_fingerprint { get; set; } public string peer_fp_algo { get; set; } private CredentialsCapsule credentials; private Cond buffer_cond = Cond(); private Mutex buffer_mutex = Mutex(); private Gee.LinkedList buffer_queue = new Gee.LinkedList(); private bool running = false; private bool stop = false; private bool restart = false; private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session(); public Handler.with_cert(CredentialsCapsule creds) { this.credentials = creds; this.own_fingerprint = creds.own_fingerprint; } public uint8[]? process_incoming_data(uint component_id, uint8[] data) throws Crypto.Error { if (data[0] >= 128) { if (!srtp_session.has_decrypt) { debug("Received data before SRTP session is ready, dropping."); return null; } if (component_id == 1) { if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { return srtp_session.decrypt_rtcp(data); } return srtp_session.decrypt_rtp(data); } if (component_id == 2) return srtp_session.decrypt_rtcp(data); } if (component_id == 1 && data.length >= 1 && (data[0] >= 20 && data[0] < 64)) { on_data_rec(data); return null; } debug("Dropping unknown data from component %u", component_id); return null; } public uint8[]? process_outgoing_data(uint component_id, uint8[] data) throws Crypto.Error { if (srtp_session.has_encrypt) { if (component_id == 1) { if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { return srtp_session.encrypt_rtcp(data); } return srtp_session.encrypt_rtp(data); } if (component_id == 2) return srtp_session.encrypt_rtcp(data); } return null; } public void on_data_rec(owned uint8[] data) { buffer_mutex.lock(); buffer_queue.add(new Bytes.take(data)); buffer_cond.signal(); buffer_mutex.unlock(); } internal static CredentialsCapsule generate_credentials() throws GLib.Error { int err = 0; X509.PrivateKey private_key = X509.PrivateKey.create(); err = private_key.generate(PKAlgorithm.ECDSA, 256); throw_if_error(err); var start_time = new DateTime.now_local().add_days(-1); var end_time = start_time.add_days(2); X509.Certificate cert = X509.Certificate.create(); cert.set_key(private_key); cert.set_version(1); cert.set_activation_time ((time_t) start_time.to_unix ()); cert.set_expiration_time ((time_t) end_time.to_unix ()); uint32 serial = 1; cert.set_serial(&serial, sizeof(uint32)); cert.sign(cert, private_key); uint8[] own_fingerprint = get_fingerprint(cert, DigestAlgorithm.SHA256); X509.Certificate[] own_cert = new X509.Certificate[] { (owned)cert }; var creds = new CredentialsCapsule(); creds.own_fingerprint = own_fingerprint; creds.own_cert = (owned) own_cert; creds.private_key = (owned) private_key; return creds; } public void stop_dtls_connection() { buffer_mutex.lock(); stop = true; buffer_cond.signal(); buffer_mutex.unlock(); } public async Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection() { MainContext context = MainContext.current_source().get_context(); var thread = new Thread("dtls-connection", () => { var res = setup_dtls_connection_thread(); Source source = new IdleSource(); source.set_callback(setup_dtls_connection.callback); source.attach(context); return res; }); yield; return thread.join(); } private Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection_thread() { buffer_mutex.lock(); if (stop) { restart = true; buffer_mutex.unlock(); return null; } if (running || ready) { buffer_mutex.unlock(); return null; } running = true; restart = false; buffer_mutex.unlock(); InitFlags server_or_client = mode == Mode.SERVER ? InitFlags.SERVER : InitFlags.CLIENT; debug("Setting up DTLS connection. We're %s", mode.to_string()); CertificateCredentials cert_cred = CertificateCredentials.create(); int err = cert_cred.set_x509_key(credentials.own_cert, credentials.private_key); throw_if_error(err); Session? session = Session.create(server_or_client | InitFlags.DATAGRAM); session.enable_heartbeat(1); session.set_srtp_profile_direct("SRTP_AES128_CM_HMAC_SHA1_80"); session.set_credentials(GnuTLS.CredentialsType.CERTIFICATE, cert_cred); session.server_set_request(CertificateRequest.REQUEST); session.set_priority_from_string("NORMAL:!VERS-TLS-ALL:+VERS-DTLS-ALL:+CTYPE-CLI-X509"); session.set_transport_pointer(this); session.set_pull_function(pull_function); session.set_pull_timeout_function(pull_timeout_function); session.set_push_function(push_function); session.set_verify_function(verify_function); DateTime maximum_time = new DateTime.now_utc().add_seconds(20); do { err = session.handshake(); DateTime current_time = new DateTime.now_utc(); if (maximum_time.compare(current_time) < 0) { warning("DTLS handshake timeouted"); err = ErrorCode.APPLICATION_ERROR_MIN + 1; break; } if (stop) { debug("DTLS handshake stopped"); err = ErrorCode.APPLICATION_ERROR_MIN + 2; break; } } while (err < 0 && !((ErrorCode)err).is_fatal()); buffer_mutex.lock(); if (stop) { stop = false; running = false; bool restart = restart; buffer_mutex.unlock(); if (restart) { debug("Restarting DTLS handshake"); return setup_dtls_connection_thread(); } return null; } buffer_mutex.unlock(); if (err != ErrorCode.SUCCESS) { warning("DTLS handshake failed: %s", ((ErrorCode)err).to_string()); return null; } uint8[] km = new uint8[150]; Datum? client_key, client_salt, server_key, server_salt; session.get_srtp_keys(km, km.length, out client_key, out client_salt, out server_key, out server_salt); if (client_key == null || client_salt == null || server_key == null || server_salt == null) { warning("SRTP client/server key/salt null"); } debug("Finished DTLS connection. We're %s", mode.to_string()); if (mode == Mode.SERVER) { srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); } else { srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); } return new Xmpp.Xep.Jingle.ContentEncryption(Xmpp.Xep.JingleIceUdp.DTLS_NS_URI, "DTLS-SRTP", credentials.own_fingerprint, peer_fingerprint); } private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) { Handler self = transport_ptr as Handler; self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait(self.buffer_mutex); if (self.stop) { self.buffer_mutex.unlock(); debug("DTLS handshake pull_function stopped"); return -1; } } Bytes data = self.buffer_queue.remove_at(0); self.buffer_mutex.unlock(); uint8[] data_uint8 = Bytes.unref_to_data((owned) data); Memory.copy(buffer, data_uint8, data_uint8.length); // The callback should return 0 on connection termination, a positive number indicating the number of bytes received, and -1 on error. return (ssize_t)data_uint8.length; } private static int pull_timeout_function(void* transport_ptr, uint ms) { Handler self = transport_ptr as Handler; int64 end_time = get_monotonic_time() + ms * 1000; self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait_until(self.buffer_mutex, end_time); if (self.stop) { self.buffer_mutex.unlock(); debug("DTLS handshake pull_timeout_function stopped"); return -1; } if (get_monotonic_time() > end_time) { self.buffer_mutex.unlock(); return 0; } } self.buffer_mutex.unlock(); // The callback should return 0 on timeout, a positive number if data can be received, and -1 on error. return 1; } private static ssize_t push_function(void* transport_ptr, uint8[] buffer) { Handler self = transport_ptr as Handler; self.send_data(buffer); // The callback should return a positive number indicating the bytes sent, and -1 on error. return (ssize_t)buffer.length; } private static int verify_function(Session session) { Handler self = session.get_transport_pointer() as Handler; try { bool valid = self.verify_peer_cert(session); if (!valid) { warning("DTLS certificate invalid. Aborting handshake."); return 1; } } catch (Error e) { warning("Error during DTLS certificate validation: %s. Aborting handshake.", e.message); return 1; } // The callback function should return 0 for the handshake to continue or non-zero to terminate. return 0; } private bool verify_peer_cert(Session session) throws GLib.Error { unowned Datum[] cert_datums = session.get_peer_certificates(); if (cert_datums.length == 0) { warning("No peer certs"); return false; } if (cert_datums.length > 1) warning("More than one peer cert"); X509.Certificate peer_cert = X509.Certificate.create(); peer_cert.import(ref cert_datums[0], CertificateFormat.DER); DigestAlgorithm algo; switch (peer_fp_algo) { case "sha-256": algo = DigestAlgorithm.SHA256; break; default: warning("Unkown peer fingerprint algorithm: %s", peer_fp_algo); return false; } uint8[] real_peer_fp = get_fingerprint(peer_cert, algo); if (real_peer_fp.length != this.peer_fingerprint.length) { warning("Fingerprint lengths not equal %i vs %i", real_peer_fp.length, peer_fingerprint.length); return false; } for (int i = 0; i < real_peer_fp.length; i++) { if (real_peer_fp[i] != this.peer_fingerprint[i]) { warning("First cert in peer cert list doesn't equal advertised one: %s vs %s", format_fingerprint(real_peer_fp), format_fingerprint(peer_fingerprint)); return false; } } return true; } } private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) { uint8[] buf = new uint8[512]; size_t buf_out_size = 512; certificate.get_fingerprint(digest_algo, buf, ref buf_out_size); uint8[] ret = new uint8[buf_out_size]; for (int i = 0; i < buf_out_size; i++) { ret[i] = buf[i]; } return ret; } private string format_fingerprint(uint8[] fingerprint) { var sb = new StringBuilder(); for (int i = 0; i < fingerprint.length; i++) { sb.append("%02X".printf(fingerprint[i])); if (i < fingerprint.length - 1) { sb.append(":"); } } return sb.str; } public enum Mode { CLIENT, SERVER } } dino-0.4.3/plugins/ice/src/module.vala0000644000000000000000000000453714452563620016327 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Ice.Module : JingleIceUdp.Module { public string? stun_ip = null; public uint stun_port = 0; public string? turn_ip = null; public Xep.ExternalServiceDiscovery.Service? turn_service = null; private weak Nice.Agent? agent; private HashMap cerds = new HashMap(); private Nice.Agent get_agent() { Nice.Agent? agent = this.agent; if (agent == null) { agent = new Nice.Agent(MainContext.@default(), Nice.Compatibility.RFC5245); if (stun_ip != null) { agent.stun_server = stun_ip; agent.stun_server_port = stun_port; } agent.ice_tcp = false; agent.set_software("Dino"); agent.weak_ref(agent_unweak); this.agent = agent; debug("STUN server for libnice %s %u", agent.stun_server, agent.stun_server_port); } return agent; } public override Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid) { DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid); return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid); } public override Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError { DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid); return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid, transport); } private DtlsSrtp.CredentialsCapsule? get_create_credentials(Jid local_full_jid, Jid peer_full_jid) { string from_to_id = local_full_jid.to_string() + peer_full_jid.to_string(); try { if (!cerds.has_key(from_to_id)) cerds[from_to_id] = DtlsSrtp.Handler.generate_credentials(); } catch (Error e) { warning("Error creating dtls credentials: %s", e.message); } return cerds[from_to_id]; } private void agent_unweak() { this.agent = null; } }dino-0.4.3/plugins/ice/src/plugin.vala0000644000000000000000000000632114452563620016331 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; using Xmpp.Xep; private extern const size_t NICE_ADDRESS_STRING_LEN; public class Dino.Plugins.Ice.Plugin : RootInterface, Object { public Dino.Application app; public void registered(Dino.Application app) { Nice.debug_enable(true); this.app = app; app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { list.add(new Module()); }); app.stream_interactor.stream_attached_modules.connect((account, stream) => { if (stream.get_module(Socks5Bytestreams.Module.IDENTITY) != null) { stream.get_module(Socks5Bytestreams.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses); } if (stream.get_module(JingleRawUdp.Module.IDENTITY) != null) { stream.get_module(JingleRawUdp.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses); } }); app.stream_interactor.stream_negotiated.connect(on_stream_negotiated); } private async void on_stream_negotiated(Account account, XmppStream stream) { Module? ice_udp_module = stream.get_module(JingleIceUdp.Module.IDENTITY) as Module; if (ice_udp_module == null) return; Gee.List services = yield ExternalServiceDiscovery.request_services(stream); foreach (Xep.ExternalServiceDiscovery.Service service in services) { if (service.transport == "udp" && (service.ty == "stun" || service.ty == "turn")) { InetAddress ip = yield lookup_ipv4_addess(service.host); if (ip == null) continue; if (service.ty == "stun") { debug("Server offers STUN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string()); ice_udp_module.stun_ip = ip.to_string(); ice_udp_module.stun_port = service.port; } else if (service.ty == "turn") { debug("Server offers TURN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string()); ice_udp_module.turn_ip = ip.to_string(); ice_udp_module.turn_service = service; } } } if (ice_udp_module.stun_ip == null) { InetAddress ip = yield lookup_ipv4_addess("stun.dino.im"); if (ip == null) return; debug("Using fallback STUN server: stun.dino.im:7886, resolved to %s", ip.to_string()); ice_udp_module.stun_ip = ip.to_string(); ice_udp_module.stun_port = 7886; } } public void shutdown() { // Nothing to do } private async InetAddress? lookup_ipv4_addess(string host) { try { Resolver resolver = Resolver.get_default(); GLib.List? ips = yield resolver.lookup_by_name_async(host); foreach (GLib.InetAddress ina in ips) { if (ina.get_family() != SocketFamily.IPV4) continue; return ina; } } catch (Error e) { warning("Failed looking up IP address of %s", host); } return null; } }dino-0.4.3/plugins/ice/src/register_plugin.vala0000644000000000000000000000013414452563620020231 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Ice.Plugin); } dino-0.4.3/plugins/ice/src/transport_parameters.vala0000644000000000000000000004461614452563620021323 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransportParameters { private Nice.Agent agent; private uint stream_id; private bool we_want_connection; private bool remote_credentials_set; private Map connections = new HashMap(); private DtlsSrtp.Handler? dtls_srtp_handler; private MainContext thread_context; private MainLoop thread_loop; private class DatagramConnection : Jingle.DatagramConnection { private Nice.Agent agent; private DtlsSrtp.Handler? dtls_srtp_handler; private uint stream_id; private string? error; private ulong datagram_received_id; public DatagramConnection(Nice.Agent agent, DtlsSrtp.Handler? dtls_srtp_handler, uint stream_id, uint8 component_id) { this.agent = agent; this.dtls_srtp_handler = dtls_srtp_handler; this.stream_id = stream_id; this.component_id = component_id; this.datagram_received_id = this.datagram_received.connect((datagram) => { bytes_received += datagram.length; }); } public override async void terminate(bool we_terminated, string? reason_string = null, string? reason_text = null) { yield base.terminate(we_terminated, reason_string, reason_text); this.disconnect(datagram_received_id); agent = null; dtls_srtp_handler = null; } public override void send_datagram(Bytes datagram) { if (this.agent != null && is_component_ready(agent, stream_id, component_id)) { try { if (dtls_srtp_handler != null) { uint8[] encrypted_data = dtls_srtp_handler.process_outgoing_data(component_id, datagram.get_data()); if (encrypted_data == null) return; GLib.OutputVector vector = { encrypted_data, encrypted_data.length }; GLib.OutputVector[] vectors = { vector }; Nice.OutputMessage message = { vectors }; Nice.OutputMessage[] messages = { message }; agent.send_messages_nonblocking(stream_id, component_id, messages); } else { GLib.OutputVector vector = { datagram.get_data(), datagram.get_size() }; GLib.OutputVector[] vectors = { vector }; Nice.OutputMessage message = { vectors }; Nice.OutputMessage[] messages = { message }; agent.send_messages_nonblocking(stream_id, component_id, messages); } bytes_sent += datagram.length; } catch (GLib.Error e) { warning("%s while send_datagram stream %u component %u", e.message, stream_id, component_id); } } } } public TransportParameters(Nice.Agent agent, DtlsSrtp.CredentialsCapsule? credentials, Xep.ExternalServiceDiscovery.Service? turn_service, string? turn_ip, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) { base(components, local_full_jid, peer_full_jid, node); this.we_want_connection = (node == null); this.agent = agent; if (this.peer_fingerprint != null || !incoming) { dtls_srtp_handler = setup_dtls(this, credentials); own_fingerprint = dtls_srtp_handler.own_fingerprint; if (incoming) { own_setup = "active"; dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } else { own_setup = "actpass"; dtls_srtp_handler.mode = DtlsSrtp.Mode.SERVER; dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (content_encryption != null) { this.content.encryptions[content_encryption.encryption_ns] = content_encryption; } }); } } agent.candidate_gathering_done.connect(on_candidate_gathering_done); agent.initial_binding_request_received.connect(on_initial_binding_request_received); agent.component_state_changed.connect(on_component_state_changed); agent.new_selected_pair_full.connect(on_new_selected_pair_full); agent.new_candidate_full.connect(on_new_candidate); agent.controlling_mode = !incoming; stream_id = agent.add_stream(components); thread_context = new MainContext(); new Thread(@"ice-thread-$stream_id", () => { thread_context.push_thread_default(); thread_loop = new MainLoop(thread_context, false); thread_loop.run(); thread_context.pop_thread_default(); return null; }); if (turn_ip != null) { for (uint8 component_id = 1; component_id <= components; component_id++) { agent.set_relay_info(stream_id, component_id, turn_ip, turn_service.port, turn_service.username, turn_service.password, Nice.RelayType.UDP); debug("TURN info (component %i) %s:%u", component_id, turn_ip, turn_service.port); } } string ufrag; string pwd; agent.get_local_credentials(stream_id, out ufrag, out pwd); init(ufrag, pwd); for (uint8 component_id = 1; component_id <= components; component_id++) { // We don't properly get local candidates before this call agent.attach_recv(stream_id, component_id, thread_context, on_recv); } agent.gather_candidates(stream_id); } private static DtlsSrtp.Handler setup_dtls(TransportParameters tp, DtlsSrtp.CredentialsCapsule credentials) { var weak_self = WeakRef(tp); DtlsSrtp.Handler dtls_srtp = new DtlsSrtp.Handler.with_cert(credentials); dtls_srtp.send_data.connect((data) => { TransportParameters self = (TransportParameters) weak_self.get(); if (self != null) self.agent.send(self.stream_id, 1, data); }); return dtls_srtp; } private void on_candidate_gathering_done(uint stream_id) { if (stream_id != this.stream_id) return; debug("on_candidate_gathering_done in %u", stream_id); for (uint8 i = 1; i <= components; i++) { foreach (unowned Nice.Candidate nc in agent.get_local_candidates(stream_id, i)) { if (nc.transport == Nice.CandidateTransport.UDP) { JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc); if (candidate == null) continue; debug("Local candidate summary: %s", agent.generate_local_candidate_sdp(nc)); } } } } private void on_new_candidate(Nice.Candidate nc) { if (nc.stream_id != stream_id) return; JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc); if (candidate == null) return; if (nc.transport == Nice.CandidateTransport.UDP) { // Execution was in the agent thread before add_local_candidate_threadsafe(candidate); } } private bool bytes_equal(uint8[] a1, uint8[] a2) { return a1.length == a2.length && Memory.cmp(a1, a2, a1.length) == 0; } public override void handle_transport_accept(StanzaNode transport) throws Jingle.IqError { debug("on_transport_accept from %s", peer_full_jid.to_string()); base.handle_transport_accept(transport); if (dtls_srtp_handler != null && peer_fingerprint != null) { if (dtls_srtp_handler.peer_fingerprint != null) { if (!bytes_equal(dtls_srtp_handler.peer_fingerprint, peer_fingerprint)) { warning("Tried to replace certificate fingerprint mid use. We don't allow that."); peer_fingerprint = dtls_srtp_handler.peer_fingerprint; peer_fp_algo = dtls_srtp_handler.peer_fp_algo; } } else { dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } if (peer_setup == "passive") { dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; dtls_srtp_handler.stop_dtls_connection(); dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (content_encryption != null) { this.content.encryptions[content_encryption.encryption_ns] = content_encryption; } }); } } else { dtls_srtp_handler = null; } } public override void handle_transport_info(StanzaNode transport) throws Jingle.IqError { debug("on_transport_info from %s", peer_full_jid.to_string()); base.handle_transport_info(transport); if (dtls_srtp_handler != null && peer_fingerprint != null) { if (dtls_srtp_handler.peer_fingerprint != null) { if (!bytes_equal(dtls_srtp_handler.peer_fingerprint, peer_fingerprint)) { warning("Tried to replace certificate fingerprint mid use. We don't allow that."); peer_fingerprint = dtls_srtp_handler.peer_fingerprint; peer_fp_algo = dtls_srtp_handler.peer_fp_algo; } } else { dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } } if (!we_want_connection) return; if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) { agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd); remote_credentials_set = true; } for (uint8 i = 1; i <= components; i++) { SList candidates = new SList(); foreach (JingleIceUdp.Candidate candidate in remote_candidates) { if (candidate.component == i) { candidates.append(candidate_to_nice(candidate)); } } int new_candidates = agent.set_remote_candidates(stream_id, i, candidates); debug("Updated to %i remote candidates for candidate %u via transport info", new_candidates, i); } } public override void create_transport_connection(XmppStream stream, Jingle.Content content) { debug("create_transport_connection: %s", content.session.sid); debug("local_credentials: %s %s", local_ufrag, local_pwd); debug("remote_credentials: %s %s", remote_ufrag, remote_pwd); debug("expected incoming credentials: %s %s", local_ufrag + ":" + remote_ufrag, local_pwd); debug("expected outgoing credentials: %s %s", remote_ufrag + ":" + local_ufrag, remote_pwd); we_want_connection = true; if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) { agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd); remote_credentials_set = true; } for (uint8 i = 1; i <= components; i++) { SList candidates = new SList(); foreach (JingleIceUdp.Candidate candidate in remote_candidates) { if (candidate.ip.has_prefix("fe80::")) continue; if (candidate.component == i) { candidates.append(candidate_to_nice(candidate)); debug("remote candidate: %s", agent.generate_local_candidate_sdp(candidate_to_nice(candidate))); } } int new_candidates = agent.set_remote_candidates(stream_id, i, candidates); debug("Initiated component %u with %i remote candidates", i, new_candidates); connections[i] = new DatagramConnection(agent, dtls_srtp_handler, stream_id, i); content.set_transport_connection(connections[i], i); } base.create_transport_connection(stream, content); } private void on_component_state_changed(uint stream_id, uint component_id, uint state) { if (stream_id != this.stream_id) return; debug("stream %u component %u state changed to %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string()); may_consider_ready(stream_id, component_id); if (incoming && dtls_srtp_handler != null && !dtls_srtp_handler.ready && is_component_ready(agent, stream_id, component_id) && dtls_srtp_handler.mode == DtlsSrtp.Mode.CLIENT) { dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { Jingle.ContentEncryption? encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (encryption != null) { this.content.encryptions[encryption.encryption_ns] = encryption; } }); } } private void may_consider_ready(uint stream_id, uint component_id) { if (stream_id != this.stream_id) return; if (connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready && is_component_ready(agent, stream_id, component_id) && (dtls_srtp_handler == null || dtls_srtp_handler.ready)) { connections[(uint8)component_id].ready = true; } } private void on_initial_binding_request_received(uint stream_id) { if (stream_id != this.stream_id) return; debug("initial_binding_request_received"); } private void on_new_selected_pair_full(uint stream_id, uint component_id, Nice.Candidate p1, Nice.Candidate p2) { if (stream_id != this.stream_id) return; debug("new_selected_pair_full %u [%s, %s]", component_id, agent.generate_local_candidate_sdp(p1), agent.generate_local_candidate_sdp(p2)); } private void on_recv(Nice.Agent agent, uint stream_id, uint component_id, uint8[] data) { if (stream_id != this.stream_id) return; uint8[] decrypt_data = null; if (dtls_srtp_handler != null) { try { decrypt_data = dtls_srtp_handler.process_incoming_data(component_id, data); if (decrypt_data == null) return; } catch (Crypto.Error e) { warning("%s while on_recv stream %u component %u", e.message, stream_id, component_id); return; } } may_consider_ready(stream_id, component_id); if (connections.has_key((uint8) component_id)) { if (!connections[(uint8) component_id].ready) { debug("on_recv stream %u component %u when state %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string()); } connections[(uint8) component_id].datagram_received(new Bytes(decrypt_data ?? data)); } else { debug("on_recv stream %u component %u length %u", stream_id, component_id, data.length); } } private static Nice.Candidate candidate_to_nice(JingleIceUdp.Candidate c) { Nice.CandidateType type; switch (c.type_) { case JingleIceUdp.Candidate.Type.HOST: type = Nice.CandidateType.HOST; break; case JingleIceUdp.Candidate.Type.PRFLX: type = Nice.CandidateType.PEER_REFLEXIVE; break; case JingleIceUdp.Candidate.Type.RELAY: type = Nice.CandidateType.RELAYED; break; case JingleIceUdp.Candidate.Type.SRFLX: type = Nice.CandidateType.SERVER_REFLEXIVE; break; default: assert_not_reached(); } Nice.Candidate candidate = new Nice.Candidate(type); candidate.component_id = c.component; char[] foundation = new char[Nice.CANDIDATE_MAX_FOUNDATION]; Memory.copy(foundation, c.foundation.data, size_t.min(c.foundation.length, Nice.CANDIDATE_MAX_FOUNDATION - 1)); candidate.foundation = foundation; candidate.addr = Nice.Address(); candidate.addr.init(); candidate.addr.set_from_string(c.ip); candidate.addr.set_port(c.port); candidate.priority = c.priority; if (c.rel_addr != null) { candidate.base_addr = Nice.Address(); candidate.base_addr.init(); candidate.base_addr.set_from_string(c.rel_addr); candidate.base_addr.set_port(c.rel_port); } candidate.transport = Nice.CandidateTransport.UDP; return candidate; } private static JingleIceUdp.Candidate? candidate_to_jingle(Nice.Candidate nc) { JingleIceUdp.Candidate candidate = new JingleIceUdp.Candidate(); switch (nc.type) { case Nice.CandidateType.HOST: candidate.type_ = JingleIceUdp.Candidate.Type.HOST; break; case Nice.CandidateType.PEER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.PRFLX; break; case Nice.CandidateType.RELAYED: candidate.type_ = JingleIceUdp.Candidate.Type.RELAY; break; case Nice.CandidateType.SERVER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.SRFLX; break; default: assert_not_reached(); } candidate.component = (uint8) nc.component_id; candidate.foundation = ((string)nc.foundation).dup(); candidate.generation = 0; candidate.id = Random.next_int().to_string("%08x"); // TODO char[] res = new char[NICE_ADDRESS_STRING_LEN]; nc.addr.to_string(res); candidate.ip = (string) res; candidate.network = 0; // TODO candidate.port = (uint16) nc.addr.get_port(); candidate.priority = nc.priority; candidate.protocol = "udp"; if (nc.base_addr.is_valid() && !nc.base_addr.equal(nc.addr)) { res = new char[NICE_ADDRESS_STRING_LEN]; nc.base_addr.to_string(res); candidate.rel_addr = (string) res; candidate.rel_port = (uint16) nc.base_addr.get_port(); } if (candidate.ip.has_prefix("fe80::")) return null; return candidate; } public override void dispose() { base.dispose(); agent = null; dtls_srtp_handler = null; connections.clear(); if (thread_loop != null) { thread_loop.quit(); } } } dino-0.4.3/plugins/ice/src/util.vala0000644000000000000000000000103414452563620016004 0ustar rootrootusing Gee; namespace Dino.Plugins.Ice { internal static bool is_component_ready(Nice.Agent agent, uint stream_id, uint component_id) { var state = agent.get_component_state(stream_id, component_id); return state == Nice.ComponentState.CONNECTED || state == Nice.ComponentState.READY; } internal Gee.List get_local_ip_addresses() { Gee.List result = new ArrayList(); foreach (string ip_address in Nice.interfaces_get_local_ips(false)) { result.add(ip_address); } return result; } }dino-0.4.3/plugins/ice/vapi/0000755000000000000000000000000014452563620014334 5ustar rootrootdino-0.4.3/plugins/ice/vapi/gnutls.vapi0000644000000000000000000003671514452563620016545 0ustar rootroot[CCode (cprefix = "gnutls_", lower_case_cprefix = "gnutls_", cheader_filename = "gnutls/gnutls.h")] namespace GnuTLS { public int global_init(); [CCode (cname = "gnutls_pull_func", has_target = false)] public delegate ssize_t PullFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array); [CCode (cname = "gnutls_pull_timeout_func", has_target = false)] public delegate int PullTimeoutFunc(void* transport_ptr, uint ms); [CCode (cname = "gnutls_push_func", has_target = false)] public delegate ssize_t PushFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array); [CCode (cname = "gnutls_certificate_verify_function", has_target = false)] public delegate int VerifyFunc(Session session); [Compact] [CCode (cname = "struct gnutls_session_int", free_function = "gnutls_deinit")] public class Session { public static Session? create(int con_end) throws GLib.Error { Session result; var ret = init(out result, con_end); throw_if_error(ret); return result; } [CCode (cname = "gnutls_init")] private static int init(out Session session, int con_end); [CCode (cname = "gnutls_transport_set_push_function")] public void set_push_function(PushFunc func); [CCode (cname = "gnutls_transport_set_pull_function")] public void set_pull_function(PullFunc func); [CCode (cname = "gnutls_transport_set_pull_timeout_function")] public void set_pull_timeout_function(PullTimeoutFunc func); [CCode (cname = "gnutls_transport_set_ptr")] public void set_transport_pointer(void* ptr); [CCode (cname = "gnutls_transport_get_ptr")] public void* get_transport_pointer(); [CCode (cname = "gnutls_heartbeat_enable")] public int enable_heartbeat(uint type); [CCode (cname = "gnutls_certificate_server_set_request")] public void server_set_request(CertificateRequest req); [CCode (cname = "gnutls_credentials_set")] public int set_credentials_(CredentialsType type, void* cred); [CCode (cname = "gnutls_credentials_set_")] public void set_credentials(CredentialsType type, void* cred) throws GLib.Error { int err = set_credentials_(type, cred); throw_if_error(err); } [CCode (cname = "gnutls_priority_set_direct")] public int set_priority_from_string_(string priority, out unowned string err_pos = null); [CCode (cname = "gnutls_priority_set_direct_")] public void set_priority_from_string(string priority, out unowned string err_pos = null) throws GLib.Error { int err = set_priority_from_string_(priority, out err_pos); throw_if_error(err); } [CCode (cname = "gnutls_srtp_set_profile_direct")] public int set_srtp_profile_direct_(string profiles, out unowned string err_pos = null); [CCode (cname = "gnutls_srtp_set_profile_direct_")] public void set_srtp_profile_direct(string profiles, out unowned string err_pos = null) throws GLib.Error { int err = set_srtp_profile_direct_(profiles, out err_pos); throw_if_error(err); } [CCode (cname = "gnutls_transport_set_int")] public void transport_set_int(int fd); [CCode (cname = "gnutls_handshake")] public int handshake(); [CCode (cname = "gnutls_srtp_get_keys")] public int get_srtp_keys_(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt); [CCode (cname = "gnutls_srtp_get_keys_")] public void get_srtp_keys(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt) throws GLib.Error { get_srtp_keys_(key_material, key_material_size, out client_key, out client_salt, out server_key, out server_salt); } [CCode (cname = "gnutls_certificate_get_peers", array_length_type = "unsigned int")] public unowned Datum[]? get_peer_certificates(); [CCode (cname = "gnutls_session_set_verify_function")] public void set_verify_function(VerifyFunc func); } [Compact] [CCode (cname = "struct gnutls_certificate_credentials_st", free_function = "gnutls_certificate_free_credentials", cprefix = "gnutls_certificate_")] public class CertificateCredentials { [CCode (cname = "gnutls_certificate_allocate_credentials")] private static int allocate(out CertificateCredentials credentials); public static CertificateCredentials create() throws GLib.Error { CertificateCredentials result; var ret = allocate (out result); throw_if_error(ret); return result; } public void get_x509_crt(uint index, [CCode (array_length_type = "unsigned int")] out unowned X509.Certificate[] x509_ca_list); public int set_x509_key(X509.Certificate[] cert_list, X509.PrivateKey key); } [CCode (cheader_filename = "gnutls/x509.h", cprefix = "GNUTLS_")] namespace X509 { [Compact] [CCode (cname = "struct gnutls_x509_crt_int", cprefix = "gnutls_x509_crt_", free_function = "gnutls_x509_crt_deinit")] public class Certificate { [CCode (cname = "gnutls_x509_crt_init")] private static int init (out Certificate cert); public static Certificate create() throws GLib.Error { Certificate result; var ret = init (out result); throw_if_error(ret); return result; } [CCode (cname = "gnutls_x509_crt_import")] public int import_(ref Datum data, CertificateFormat format); [CCode (cname = "gnutls_x509_crt_import_")] public void import(ref Datum data, CertificateFormat format) throws GLib.Error { int err = import_(ref data, format); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_version")] public int set_version_(uint version); [CCode (cname = "gnutls_x509_crt_set_version_")] public void set_version(uint version) throws GLib.Error { int err = set_version_(version); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_key")] public int set_key_(PrivateKey key); [CCode (cname = "gnutls_x509_crt_set_key_")] public void set_key(PrivateKey key) throws GLib.Error { int err = set_key_(key); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_activation_time")] public int set_activation_time_(time_t act_time); [CCode (cname = "gnutls_x509_crt_set_activation_time_")] public void set_activation_time(time_t act_time) throws GLib.Error { int err = set_activation_time_(act_time); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_expiration_time")] public int set_expiration_time_(time_t exp_time); [CCode (cname = "gnutls_x509_crt_set_expiration_time_")] public void set_expiration_time(time_t exp_time) throws GLib.Error { int err = set_expiration_time_(exp_time); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_serial")] public int set_serial_(void* serial, size_t serial_size); [CCode (cname = "gnutls_x509_crt_set_serial_")] public void set_serial(void* serial, size_t serial_size) throws GLib.Error { int err = set_serial_(serial, serial_size); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_sign")] public int sign_(Certificate issuer, PrivateKey issuer_key); [CCode (cname = "gnutls_x509_crt_sign_")] public void sign(Certificate issuer, PrivateKey issuer_key) throws GLib.Error { int err = sign_(issuer, issuer_key); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_get_fingerprint")] public int get_fingerprint_(DigestAlgorithm algo, void* buf, ref size_t buf_size); [CCode (cname = "gnutls_x509_crt_get_fingerprint_")] public void get_fingerprint(DigestAlgorithm algo, void* buf, ref size_t buf_size) throws GLib.Error { int err = get_fingerprint_(algo, buf, ref buf_size); throw_if_error(err); } } [Compact] [CCode (cname = "struct gnutls_x509_privkey_int", cprefix = "gnutls_x509_privkey_", free_function = "gnutls_x509_privkey_deinit")] public class PrivateKey { private static int init (out PrivateKey key); public static PrivateKey create () throws GLib.Error { PrivateKey result; var ret = init (out result); throw_if_error(ret); return result; } public int generate(PKAlgorithm algo, uint bits, uint flags = 0); } } [CCode (cname = "gnutls_certificate_request_t", cprefix = "GNUTLS_CERT_", has_type_id = false)] public enum CertificateRequest { IGNORE, REQUEST, REQUIRE } [CCode (cname = "gnutls_pk_algorithm_t", cprefix = "GNUTLS_PK_", has_type_id = false)] public enum PKAlgorithm { UNKNOWN, RSA, DSA, ECDSA; } [CCode (cname = "gnutls_digest_algorithm_t", cprefix = "GNUTLS_DIG_", has_type_id = false)] public enum DigestAlgorithm { NULL, MD5, SHA1, RMD160, MD2, SHA224, SHA256, SHA384, SHA512; } [Flags] [CCode (cname = "gnutls_init_flags_t", cprefix = "GNUTLS_", has_type_id = false)] public enum InitFlags { SERVER, CLIENT, DATAGRAM } [CCode (cname = "gnutls_credentials_type_t", cprefix = "GNUTLS_CRD_", has_type_id = false)] public enum CredentialsType { CERTIFICATE, ANON, SRP, PSK, IA } [CCode (cname = "gnutls_x509_crt_fmt_t", cprefix = "GNUTLS_X509_FMT_", has_type_id = false)] public enum CertificateFormat { DER, PEM } [Flags] [CCode (cname = "gnutls_certificate_status_t", cprefix = "GNUTLS_CERT_", has_type_id = false)] public enum CertificateStatus { INVALID, // will be set if the certificate was not verified. REVOKED, // in X.509 this will be set only if CRLs are checked SIGNER_NOT_FOUND, SIGNER_NOT_CA, INSECURE_ALGORITHM } [SimpleType] [CCode (cname = "gnutls_datum_t", has_type_id = false)] public struct Datum { public uint8* data; public uint size; public uint8[] extract() { uint8[] ret = new uint8[size]; for (int i = 0; i < size; i++) { ret[i] = data[i]; } return ret; } } // Gnutls error codes. The mapping to a TLS alert is also shown in comments. [CCode (cname = "int", cprefix = "GNUTLS_E_", lower_case_cprefix = "gnutls_error_", has_type_id = false)] public enum ErrorCode { SUCCESS, UNKNOWN_COMPRESSION_ALGORITHM, UNKNOWN_CIPHER_TYPE, LARGE_PACKET, UNSUPPORTED_VERSION_PACKET, // GNUTLS_A_PROTOCOL_VERSION UNEXPECTED_PACKET_LENGTH, // GNUTLS_A_RECORD_OVERFLOW INVALID_SESSION, FATAL_ALERT_RECEIVED, UNEXPECTED_PACKET, // GNUTLS_A_UNEXPECTED_MESSAGE WARNING_ALERT_RECEIVED, ERROR_IN_FINISHED_PACKET, UNEXPECTED_HANDSHAKE_PACKET, UNKNOWN_CIPHER_SUITE, // GNUTLS_A_HANDSHAKE_FAILURE UNWANTED_ALGORITHM, MPI_SCAN_FAILED, DECRYPTION_FAILED, // GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_BAD_RECORD_MAC MEMORY_ERROR, DECOMPRESSION_FAILED, // GNUTLS_A_DECOMPRESSION_FAILURE COMPRESSION_FAILED, AGAIN, EXPIRED, DB_ERROR, SRP_PWD_ERROR, INSUFFICIENT_CREDENTIALS, HASH_FAILED, BASE64_DECODING_ERROR, MPI_PRINT_FAILED, REHANDSHAKE, // GNUTLS_A_NO_RENEGOTIATION GOT_APPLICATION_DATA, RECORD_LIMIT_REACHED, ENCRYPTION_FAILED, PK_ENCRYPTION_FAILED, PK_DECRYPTION_FAILED, PK_SIGN_FAILED, X509_UNSUPPORTED_CRITICAL_EXTENSION, KEY_USAGE_VIOLATION, NO_CERTIFICATE_FOUND, // GNUTLS_A_BAD_CERTIFICATE INVALID_REQUEST, SHORT_MEMORY_BUFFER, INTERRUPTED, PUSH_ERROR, PULL_ERROR, RECEIVED_ILLEGAL_PARAMETER, // GNUTLS_A_ILLEGAL_PARAMETER REQUESTED_DATA_NOT_AVAILABLE, PKCS1_WRONG_PAD, RECEIVED_ILLEGAL_EXTENSION, INTERNAL_ERROR, DH_PRIME_UNACCEPTABLE, FILE_ERROR, TOO_MANY_EMPTY_PACKETS, UNKNOWN_PK_ALGORITHM, // returned if libextra functionality was requested but // gnutls_global_init_extra() was not called. INIT_LIBEXTRA, LIBRARY_VERSION_MISMATCH, // returned if you need to generate temporary RSA // parameters. These are needed for export cipher suites. NO_TEMPORARY_RSA_PARAMS, LZO_INIT_FAILED, NO_COMPRESSION_ALGORITHMS, NO_CIPHER_SUITES, OPENPGP_GETKEY_FAILED, PK_SIG_VERIFY_FAILED, ILLEGAL_SRP_USERNAME, SRP_PWD_PARSING_ERROR, NO_TEMPORARY_DH_PARAMS, // For certificate and key stuff ASN1_ELEMENT_NOT_FOUND, ASN1_IDENTIFIER_NOT_FOUND, ASN1_DER_ERROR, ASN1_VALUE_NOT_FOUND, ASN1_GENERIC_ERROR, ASN1_VALUE_NOT_VALID, ASN1_TAG_ERROR, ASN1_TAG_IMPLICIT, ASN1_TYPE_ANY_ERROR, ASN1_SYNTAX_ERROR, ASN1_DER_OVERFLOW, OPENPGP_UID_REVOKED, CERTIFICATE_ERROR, CERTIFICATE_KEY_MISMATCH, UNSUPPORTED_CERTIFICATE_TYPE, // GNUTLS_A_UNSUPPORTED_CERTIFICATE X509_UNKNOWN_SAN, OPENPGP_FINGERPRINT_UNSUPPORTED, X509_UNSUPPORTED_ATTRIBUTE, UNKNOWN_HASH_ALGORITHM, UNKNOWN_PKCS_CONTENT_TYPE, UNKNOWN_PKCS_BAG_TYPE, INVALID_PASSWORD, MAC_VERIFY_FAILED, // for PKCS #12 MAC CONSTRAINT_ERROR, WARNING_IA_IPHF_RECEIVED, WARNING_IA_FPHF_RECEIVED, IA_VERIFY_FAILED, UNKNOWN_ALGORITHM, BASE64_ENCODING_ERROR, INCOMPATIBLE_CRYPTO_LIBRARY, INCOMPATIBLE_LIBTASN1_LIBRARY, OPENPGP_KEYRING_ERROR, X509_UNSUPPORTED_OID, RANDOM_FAILED, BASE64_UNEXPECTED_HEADER_ERROR, OPENPGP_SUBKEY_ERROR, CRYPTO_ALREADY_REGISTERED, HANDSHAKE_TOO_LARGE, UNIMPLEMENTED_FEATURE, APPLICATION_ERROR_MAX, // -65000 APPLICATION_ERROR_MIN; // -65500 [CCode (cname = "gnutls_error_is_fatal")] public bool is_fatal(); [CCode (cname = "gnutls_perror")] public void print(); [CCode (cname = "gnutls_strerror")] public unowned string to_string(); } public void throw_if_error(int err_int) throws GLib.Error { ErrorCode error = (ErrorCode)err_int; if (error != ErrorCode.SUCCESS) { throw new GLib.Error(-1, error, "%s%s", error.to_string(), error.is_fatal() ? " fatal" : ""); } } }dino-0.4.3/plugins/ice/vapi/metadata/0000755000000000000000000000000014452563620016114 5ustar rootrootdino-0.4.3/plugins/ice/vapi/metadata/Nice-0.1.metadata0000644000000000000000000000066414452563620020776 0ustar rootrootNice cheader_filename="nice.h" Address.to_string.dst type="char[]" Agent.new_reliable#constructor name="create_reliable" Agent.attach_recv skip=false Agent.send.buf type="uint8[]" array_length_idx=2 AgentRecvFunc.buf type="uint8[]" array_length_idx=3 PseudoTcpCallbacks#record skip PseudoTcpSocket#class skip # Not yet supported by vapigen # Candidate copy_function="nice_candidate_copy" free_function="nice_candidate_free" type_id="" dino-0.4.3/plugins/ice/vapi/nice.vapi0000644000000000000000000004100014452563620016126 0ustar rootroot/* nice.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Nice", gir_namespace = "Nice", gir_version = "0.1", lower_case_cprefix = "nice_")] namespace Nice { [CCode (cheader_filename = "nice.h", type_id = "nice_agent_get_type ()")] public class Agent : GLib.Object { [CCode (has_construct_function = false)] public Agent (GLib.MainContext ctx, Nice.Compatibility compat); public bool add_local_address (Nice.Address addr); public uint add_stream (uint n_components); public bool attach_recv (uint stream_id, uint component_id, GLib.MainContext ctx, Nice.AgentRecvFunc func); [Version (since = "0.1.16")] public async void close_async (); [CCode (cname = "nice_agent_new_reliable", has_construct_function = false)] [Version (since = "0.0.11")] public Agent.create_reliable (GLib.MainContext ctx, Nice.Compatibility compat); [Version (since = "0.1.6")] public bool forget_relays (uint stream_id, uint component_id); [CCode (has_construct_function = false)] [Version (since = "0.1.15")] public Agent.full (GLib.MainContext ctx, Nice.Compatibility compat, Nice.AgentOption flags); public bool gather_candidates (uint stream_id); [Version (since = "0.1.4")] public string generate_local_candidate_sdp (Nice.Candidate candidate); [Version (since = "0.1.4")] public string generate_local_sdp (); [Version (since = "0.1.4")] public string generate_local_stream_sdp (uint stream_id, bool include_non_ice); [Version (since = "0.1.8")] public Nice.ComponentState get_component_state (uint stream_id, uint component_id); public Nice.Candidate get_default_local_candidate (uint stream_id, uint component_id); [Version (since = "0.1.5")] public GLib.IOStream get_io_stream (uint stream_id, uint component_id); public GLib.SList get_local_candidates (uint stream_id, uint component_id); public bool get_local_credentials (uint stream_id, out string ufrag, out string pwd); public GLib.SList get_remote_candidates (uint stream_id, uint component_id); public bool get_selected_pair (uint stream_id, uint component_id, Nice.Candidate local, Nice.Candidate remote); [Version (since = "0.1.5")] public GLib.Socket? get_selected_socket (uint stream_id, uint component_id); [Version (since = "0.1.4")] public unowned string get_stream_name (uint stream_id); [Version (since = "0.1.4")] public Nice.Candidate parse_remote_candidate_sdp (uint stream_id, string sdp); [Version (since = "0.1.4")] public int parse_remote_sdp (string sdp); [Version (since = "0.1.4")] public GLib.SList parse_remote_stream_sdp (uint stream_id, string sdp, string ufrag, string pwd); [Version (since = "0.1.16")] public bool peer_candidate_gathering_done (uint stream_id); [Version (since = "0.1.5")] public ssize_t recv (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public int recv_messages (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public int recv_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public ssize_t recv_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error; public void remove_stream (uint stream_id); public bool restart (); [Version (since = "0.1.6")] public bool restart_stream (uint stream_id); public int send (uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 2.5, array_length_type = "guint", type = "const gchar*")] uint8[] buf); [Version (since = "0.1.5")] public int send_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] Nice.OutputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; public bool set_local_credentials (uint stream_id, string ufrag, string pwd); public void set_port_range (uint stream_id, uint component_id, uint min_port, uint max_port); public bool set_relay_info (uint stream_id, uint component_id, string server_ip, uint server_port, string username, string password, Nice.RelayType type); public int set_remote_candidates (uint stream_id, uint component_id, GLib.SList candidates); public bool set_remote_credentials (uint stream_id, string ufrag, string pwd); public bool set_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation); public bool set_selected_remote_candidate (uint stream_id, uint component_id, Nice.Candidate candidate); [Version (since = "0.0.10")] public void set_software (string software); [Version (since = "0.1.4")] public bool set_stream_name (uint stream_id, string name); [Version (since = "0.0.9")] public void set_stream_tos (uint stream_id, int tos); [NoAccessorMethod] [Version (since = "0.1.8")] public bool bytestream_tcp { get; } [NoAccessorMethod] public uint compatibility { get; construct; } [NoAccessorMethod] public bool controlling_mode { get; set; } [NoAccessorMethod] [Version (since = "0.1.14")] public bool force_relay { get; set; } [NoAccessorMethod] public bool full_mode { get; construct; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool ice_tcp { get; set; } [NoAccessorMethod] [Version (since = "0.1.16")] public bool ice_trickle { get; set; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool ice_udp { get; set; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool keepalive_conncheck { get; set; } [NoAccessorMethod] public void* main_context { get; construct; } [NoAccessorMethod] public uint max_connectivity_checks { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_ip { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_password { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public uint proxy_port { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public uint proxy_type { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_username { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.11")] public bool reliable { get; construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_initial_timeout { get; set construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_max_retransmissions { get; set construct; } [NoAccessorMethod] public uint stun_pacing_timer { get; set construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_reliable_timeout { get; set construct; } [NoAccessorMethod] public string stun_server { owned get; set; } [NoAccessorMethod] public uint stun_server_port { get; set; } [NoAccessorMethod] public bool support_renomination { get; set; } [NoAccessorMethod] [Version (since = "0.0.7")] public bool upnp { get; set construct; } [NoAccessorMethod] [Version (since = "0.0.7")] public uint upnp_timeout { get; set construct; } public signal void candidate_gathering_done (uint stream_id); public signal void component_state_changed (uint stream_id, uint component_id, uint state); public signal void initial_binding_request_received (uint stream_id); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_candidate (uint stream_id, uint component_id, string foundation); [Version (since = "0.1.8")] public signal void new_candidate_full (Nice.Candidate candidate); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_remote_candidate (uint stream_id, uint component_id, string foundation); [Version (since = "0.1.8")] public signal void new_remote_candidate_full (Nice.Candidate candidate); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation); [Version (since = "0.1.8")] public signal void new_selected_pair_full (uint stream_id, uint component_id, Nice.Candidate lcandidate, Nice.Candidate rcandidate); [Version (since = "0.0.11")] public signal void reliable_transport_writable (uint stream_id, uint component_id); [Version (since = "0.1.5")] public signal void streams_removed ([CCode (array_length = false, array_null_terminated = true)] uint[] stream_ids); } [CCode (cheader_filename = "nice.h", copy_function = "nice_candidate_copy", free_function = "nice_candidate_free")] [Compact] public class Candidate { public Nice.Address addr; public Nice.Address base_addr; public uint component_id; [CCode (array_length = false)] public weak char foundation[33]; public weak string password; public uint32 priority; public void* sockptr; public uint stream_id; public Nice.CandidateTransport transport; public Nice.TurnServer turn; public Nice.CandidateType type; public weak string username; [CCode (has_construct_function = false)] public Candidate (Nice.CandidateType type); public Nice.Candidate copy (); [Version (since = "0.1.15")] public bool equal_target (Nice.Candidate candidate2); public void free (); } [CCode (cheader_filename = "nice.h", has_type_id = false)] public struct Address { [CCode (cname = "s.addr")] public void* s_addr; [CCode (cname = "s.ip4")] public void* s_ip4; [CCode (cname = "s.ip6")] public void* s_ip6; public void copy_to_sockaddr (void* sin); public bool equal (Nice.Address b); [Version (since = "0.1.8")] public bool equal_no_port (Nice.Address b); public void free (); public uint get_port (); public void init (); public int ip_version (); public bool is_private (); public bool is_valid (); public void set_from_sockaddr (void* sin); public bool set_from_string (string str); public void set_ipv4 (uint32 addr_ipv4); public void set_ipv6 (uint8 addr_ipv6); public void set_port (uint port); public void to_string ([CCode (array_length = false, type = "gchar*")] char[] dst); } [CCode (cheader_filename = "nice.h", has_type_id = false)] [Version (since = "0.1.5")] public struct InputMessage { [CCode (array_length_cname = "n_buffers")] public weak GLib.InputVector[] buffers; public int n_buffers; public Nice.Address from; public size_t length; } [CCode (cheader_filename = "nice.h", has_type_id = false)] [Version (since = "0.1.5")] public struct OutputMessage { [CCode (array_length_cname = "n_buffers")] public weak GLib.OutputVector[] buffers; public int n_buffers; } [CCode (cheader_filename = "nice.h", cname = "TurnServer", has_type_id = false)] public struct TurnServer { public int ref_count; public Nice.Address server; public weak string username; public weak string password; public Nice.RelayType type; } [CCode (cheader_filename = "nice.h", cprefix = "NICE_AGENT_OPTION_", has_type_id = false)] [Flags] [Version (since = "0.1.15")] public enum AgentOption { REGULAR_NOMINATION, RELIABLE, LITE_MODE, ICE_TRICKLE, SUPPORT_RENOMINATION } [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TRANSPORT_", has_type_id = false)] public enum CandidateTransport { UDP, TCP_ACTIVE, TCP_PASSIVE, TCP_SO } [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TYPE_", has_type_id = false)] public enum CandidateType { HOST, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPATIBILITY_", has_type_id = false)] public enum Compatibility { RFC5245, DRAFT19, GOOGLE, MSN, WLM2009, OC2007, OC2007R2, LAST } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_STATE_", has_type_id = false)] public enum ComponentState { DISCONNECTED, GATHERING, CONNECTING, CONNECTED, READY, FAILED, LAST; [Version (since = "0.1.6")] public unowned string to_string (); } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_TYPE_", has_type_id = false)] public enum ComponentType { RTP, RTCP } [CCode (cheader_filename = "nice.h", cprefix = "NICE_NOMINATION_MODE_", has_type_id = false)] [Version (since = "0.1.15")] public enum NominationMode { REGULAR, AGGRESSIVE } [CCode (cheader_filename = "nice.h", cprefix = "NICE_PROXY_TYPE_", has_type_id = false)] [Version (since = "0.0.4")] public enum ProxyType { NONE, SOCKS5, HTTP, LAST } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpDebugLevel", cprefix = "PSEUDO_TCP_DEBUG_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpDebugLevel { NONE, NORMAL, VERBOSE } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpShutdown", cprefix = "PSEUDO_TCP_SHUTDOWN_", has_type_id = false)] [Version (since = "0.1.8")] public enum PseudoTcpShutdown { RD, WR, RDWR } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpState", cprefix = "PSEUDO_TCP_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpState { LISTEN, SYN_SENT, SYN_RECEIVED, ESTABLISHED, CLOSED, FIN_WAIT_1, FIN_WAIT_2, CLOSING, TIME_WAIT, CLOSE_WAIT, LAST_ACK } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpWriteResult", cprefix = "WR_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpWriteResult { SUCCESS, TOO_LARGE, FAIL } [CCode (cheader_filename = "nice.h", cprefix = "NICE_RELAY_TYPE_TURN_", has_type_id = false)] public enum RelayType { UDP, TCP, TLS } [CCode (cheader_filename = "nice.h", instance_pos = 4.9)] public delegate void AgentRecvFunc (Nice.Agent agent, uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 3.5, array_length_type = "guint", type = "gchar*")] uint8[] buf); [CCode (cheader_filename = "nice.h", cname = "NICE_AGENT_MAX_REMOTE_CANDIDATES")] public const int AGENT_MAX_REMOTE_CANDIDATES; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_ACTIVE")] public const int CANDIDATE_DIRECTION_MS_PREF_ACTIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_PASSIVE")] public const int CANDIDATE_DIRECTION_MS_PREF_PASSIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_MAX_FOUNDATION")] public const int CANDIDATE_MAX_FOUNDATION; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_TCP")] public const int CANDIDATE_TRANSPORT_MS_PREF_TCP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP")] public const int CANDIDATE_TRANSPORT_MS_PREF_UDP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_HOST")] public const int CANDIDATE_TYPE_PREF_HOST; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED")] public const int CANDIDATE_TYPE_PREF_NAT_ASSISTED; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE")] public const int CANDIDATE_TYPE_PREF_PEER_REFLEXIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED")] public const int CANDIDATE_TYPE_PREF_RELAYED; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP")] public const int CANDIDATE_TYPE_PREF_RELAYED_UDP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE")] public const int CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE; [CCode (cheader_filename = "nice.h")] public static void debug_disable (bool with_stun); [CCode (cheader_filename = "nice.h")] public static void debug_enable (bool with_stun); [CCode (cheader_filename = "nice.h")] public static string? interfaces_get_ip_for_interface (string interface_name); [CCode (cheader_filename = "nice.h")] public static GLib.List interfaces_get_local_interfaces (); [CCode (cheader_filename = "nice.h")] public static GLib.List interfaces_get_local_ips (bool include_loopback); [CCode (cheader_filename = "nice.h", cname = "pseudo_tcp_set_debug_level")] [Version (since = "0.0.11")] public static void pseudo_tcp_set_debug_level (Nice.PseudoTcpDebugLevel level); } dino-0.4.3/plugins/notification-sound/0000755000000000000000000000000014452563620016451 5ustar rootrootdino-0.4.3/plugins/notification-sound/CMakeLists.txt0000644000000000000000000000146414452563620021216 0ustar rootrootfind_packages(NOTIFICATION_SOUND_PACKAGES REQUIRED Canberra Gee GLib GModule GObject GDKPixbuf2 ) vala_precompile(NOTIFICATION_SOUND_VALA_C SOURCES src/plugin.vala src/register_plugin.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi PACKAGES ${NOTIFICATION_SOUND_PACKAGES} ) add_definitions(${VALA_CFLAGS}) add_library(notification-sound SHARED ${NOTIFICATION_SOUND_VALA_C}) target_link_libraries(notification-sound libdino ${NOTIFICATION_SOUND_PACKAGES}) set_target_properties(notification-sound PROPERTIES PREFIX "") set_target_properties(notification-sound PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS notification-sound ${PLUGIN_INSTALL}) dino-0.4.3/plugins/notification-sound/src/0000755000000000000000000000000014452563620017240 5ustar rootrootdino-0.4.3/plugins/notification-sound/src/plugin.vala0000644000000000000000000000114314452563620021402 0ustar rootrootnamespace Dino.Plugins.NotificationSound { public class Plugin : RootInterface, Object { public Dino.Application app; private Canberra.Context sound_context; public void registered(Dino.Application app) { this.app = app; Canberra.Context.create(out sound_context); app.stream_interactor.get_module(NotificationEvents.IDENTITY).notify_content_item.connect((item, conversation) => { sound_context.play(0, Canberra.PROP_EVENT_ID, "message-new-instant", Canberra.PROP_EVENT_DESCRIPTION, "New Dino message"); }); } public void shutdown() { } } } dino-0.4.3/plugins/notification-sound/src/register_plugin.vala0000644000000000000000000000015214452563620023305 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.NotificationSound.Plugin); } dino-0.4.3/plugins/omemo/0000755000000000000000000000000014452563620013751 5ustar rootrootdino-0.4.3/plugins/omemo/CMakeLists.txt0000644000000000000000000000507614452563620016521 0ustar rootrootset(GETTEXT_PACKAGE "dino-omemo") find_package(Gettext) include(${GETTEXT_USE_FILE}) gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/po TARGET_NAME ${GETTEXT_PACKAGE}-translations) find_package(Qrencode REQUIRED) find_packages(OMEMO_PACKAGES REQUIRED Gee GLib GModule GObject GTK4 ) set(RESOURCE_LIST contact_details_dialog.ui manage_key_dialog.ui ) compile_gresources( OMEMO_GRESOURCES_TARGET OMEMO_GRESOURCES_XML TARGET ${CMAKE_CURRENT_BINARY_DIR}/resources/resources.c TYPE EMBED_C RESOURCES ${RESOURCE_LIST} PREFIX /im/dino/Dino/omemo SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data ) vala_precompile(OMEMO_VALA_C SOURCES src/dtls_srtp_verification_draft.vala src/plugin.vala src/register_plugin.vala src/trust_level.vala src/file_transfer/file_decryptor.vala src/file_transfer/file_encryptor.vala src/jingle/jingle_helper.vala src/jingle/jet_omemo.vala src/logic/database.vala src/logic/decrypt.vala src/logic/encrypt.vala src/logic/manager.vala src/logic/pre_key_store.vala src/logic/session_store.vala src/logic/signed_pre_key_store.vala src/logic/trust_manager.vala src/protocol/bundle.vala src/protocol/message_flag.vala src/protocol/stream_module.vala src/ui/account_settings_entry.vala src/ui/bad_messages_populator.vala src/ui/call_encryption_entry.vala src/ui/contact_details_provider.vala src/ui/contact_details_dialog.vala src/ui/device_notification_populator.vala src/ui/own_notifications.vala src/ui/encryption_list_entry.vala src/ui/manage_key_dialog.vala src/ui/util.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/signal-protocol.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/libqrencode.vapi PACKAGES ${OMEMO_PACKAGES} GRESOURCES ${OMEMO_GRESOURCES_XML} ) add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="OMEMO") add_library(omemo SHARED ${OMEMO_VALA_C} ${OMEMO_GRESOURCES_TARGET}) add_dependencies(omemo ${GETTEXT_PACKAGE}-translations) target_link_libraries(omemo libdino signal-protocol-vala crypto-vala ${OMEMO_PACKAGES} libqrencode) set_target_properties(omemo PROPERTIES PREFIX "") set_target_properties(omemo PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS omemo ${PLUGIN_INSTALL}) dino-0.4.3/plugins/omemo/data/0000755000000000000000000000000014452563620014662 5ustar rootrootdino-0.4.3/plugins/omemo/data/contact_details_dialog.ui0000644000000000000000000003747314452563620021716 0ustar rootroot left 10 10 10 10 False dino-0.4.3/plugins/omemo/data/manage_key_dialog.ui0000644000000000000000000002260614452563620020646 0ustar rootroot dino-0.4.3/plugins/omemo/po/0000755000000000000000000000000014452563620014367 5ustar rootrootdino-0.4.3/plugins/omemo/po/LINGUAS0000644000000000000000000000015714452563620015417 0ustar rootrootar ca cs de el en eo es eu fa fi fr gl hu id ie it ja lb lt nb nl oc pl pt pt_BR ro ru sq sv tr uk zh_CN zh_TW dino-0.4.3/plugins/omemo/po/ar.po0000644000000000000000000003100414452563620015327 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-08-31 23:44+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 4.8.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "يستخدم %s جهازًا غير موثوق به. لن ترى رسائل من أجهزة لا تثق بها." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "إدارة الأجهزة" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s لا يثق بهذا الجهاز. هذا يعني أنك قد تفقد بعض الرسائل." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "إدارة المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "قارن البصمة حرفا بحرف ، مع التي تظهر على جهاز مُراسِلك." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "البصمات غير متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "البصمات متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "ألغاء" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "تأكيد" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "التحقق مِن المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "سيتم تمييز الرسائل القادمة المرسلة بواسطة %s من الجهاز الذي يستخدم هذا " "المفتاح وفقًا لذلك في نافذة الدردشة." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "البصمات غير متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "يرجى التحقق بالمقارنة مع البصمة الصحيحة.إن عدم تطابق البصمات يعني أنّه مِن " "المحتمل أنّ حساب %s قد تعرض لاختراق وأنه مِن المستحسن التفكير في رفض هذا " "المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "التحقق مِن مفتاح البصمة" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "قارن بصمة هذا المفتاح مع البصمة المعروضة على جهاز مُراسِلك." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "رفض المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "حظر الاتصال المشفر مع جهاز المراسل الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "اقبل المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "السماح بالاتصال المشفر بجهاز المراسل الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "المفتاح الآن %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "مقبول" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "ذلك يعني أنه بإمكان %s أن يستخدمه لتلقي وإرسال رسائل مشفرة." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "تم التحقق منه" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "بالإضافة إلى أنه قد تم التحقق من تطابق المفتاح على جهاز المُراسِل." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "مرفوض" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "هذا يعني أنه لن يكون بمقدور %s استخدامه لفك تشفير رسائلك، ولن ترى الرسائل " "المشفرة به." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "لن ترى الرسائل المشفرة من جهاز %s الذي يستخدم هذا المفتاح. على العكس من " "ذلك ، لن يتمكن هذا الجهاز من فك تشفير رسائلك بعد الآن." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "سوف يكون بمقدورك تبادل الرسائل المشفرة مع جهاز %s الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "العودة" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "الإدارة" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "لدى هذا المُراسِل أجهزة جديدة" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "مطلوب قرار ثقة مع OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "هل قمت بإضافة جهاز جديد على حساب %s؟" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "بصمتك الخاصة" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "سيتم توليدها عند أول اتصال" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "التشفير" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d لا يوجد جهاز OMEMO" msgstr[1] "%d جهاز OMEMO" msgstr[2] "جهازين ل OMEMO" msgstr[3] "%d أجهزة OMEMO" msgstr[4] "%d جهاز OMEMO" msgstr[5] "%d أجهزة OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "إدارة مفاتيح أوميمو" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "تقبّل المفاتيح الجديدة تلقائياً" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "سيتم قبول مفاتيح التشفير الجديدة الخاصة بهذا المراسل تلقائيًا." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "مفتاحك الخاص" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "المفاتيح الجديدة" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "المفاتيح المقترِنة" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "المفاتيح غير المُنشَّطة" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "سيتم قبول مفاتيح التشفير الجديدة من أجهزتك الأخرى تلقائيًا." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "تم قبوله" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "تم رفضه" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "تم التحقق منه" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "غير مستخدَم" #~ msgid "Not matching" #~ msgstr "غير مطابقة" #~ msgid "Matching" #~ msgstr "مطابقة" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "بمجرد تأكيد ذلك ، سيتم تمييز أية رسائل مستقبلية مرسلة مِن %s باستخدام هذا " #~ "المفتاح وفقا لذلك في نافذة الدردشة." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "توقف عن قبول هذا المفتاح أثناء الاتصال مع المُراسِل المرتبط به." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "ابدأ في قبول هذا المفتاح أثناء الاتصال مع المُراسِل المرتبط به" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "هذا يعني أنه لا يمكن استخدامه مِن طرف %s لاستقبال الرسائل و سيتم تجاهل أية " #~ "رسائل بُعِثت به." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "بمجرد التأكيد ، سيتم تجاهل أي رسائل مستقبلية يقوم بإرسالها %s باستخدام " #~ "هذا المفتاح ويتصبح كافة رسائلك غير قابلة للقراءة باستخدام هذا المفتاح." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "بمجرد تأكيد هذا المفتاح سوف يكون قابلا للاستخدام من قِبل %s لتلقي وإرسال " #~ "الرسائل." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "عند قيام جهة الاتصال هذه باضافة مفاتيح تشفير جديدة إلى حسابها ، قم " #~ "بقبولها تلقائيا." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "عند إضافة مفاتيح تشفير جديدة إلى حسابك ، اقبلها تلقائيا." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "جهاز مجهول (0x%.8x)" #~ msgid "Other devices" #~ msgstr "أجهزة أخرى" #~ msgid "- None -" #~ msgstr "- لا شيء -" dino-0.4.3/plugins/omemo/po/ca.po0000644000000000000000000002004514452563620015313 0ustar rootroot# Catalan translation for dino-omemo. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018. # msgid "" msgstr "" "Project-Id-Version: dino-omemo 20180123\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-01-14 05:21+0000\n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestiona la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compareu l'empremta, caràcter per caràcter, amb la que es mostra al vostre " "dispositiu de contactes." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancel·la" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirma" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Comproveu la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Les empremtes no es corresponen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Cercioreu-vos que estigueu verificant l'empremta correcta. Si no en " "coincideixen, el compte de %s podria trobar-se en perill i heu de considerar " "rebutjar aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 #, fuzzy msgid "Verify key fingerprint" msgstr "Emprempta pròpia" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rebutja la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Accepta la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Actualment, aquesta clau ha estat %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rebutjada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Enrere" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestiona" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Aquest contacte té dispositius nous" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Heu afegit un dispositiu nou per al compte %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Empremta pròpia" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Es generarà durant la primera connexió" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Xifratge" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispositiu OMEMO" msgstr[1] "%d dispositius OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gestió de claus OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Accepta claus noves automàticament" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Clau pròpia" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Claus noves" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Claus associades" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Claus inactives" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Acceptada" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rebutjada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "No utilitzada" #~ msgid "Not matching" #~ msgstr "No coincideixen" #~ msgid "Matching" #~ msgstr "Coincideixen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Després de la confirmació, qualssevol missatges que enviï %s amb aquesta " #~ "clau es ressaltarà a la finestra del xat." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositiu desconegut (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Altres dispositius" #~ msgid "- None -" #~ msgstr "- Cap -" dino-0.4.3/plugins/omemo/po/cs.po0000644000000000000000000002200214452563620015330 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-07-05 16:32+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s používá nedůvěryhodné zařízení. Zprávy z nedůvěryhodných zařízení nebudou " "zobrazeny." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Správa zařízení" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nedůvěřuje tomuto zařízení. Z tohoto důvodu nemusí vidět některé zprávy." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Spravovat klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Porovnejte otisk klíče, znak po znaku, s tím, který je zobrazen na zařízení " "vašeho kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Otisky klíčů se liší" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Otisky klíčů odpovídají" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Zrušit" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Potvrdit" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Ověřit klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Budoucí zprávy, s odesílatelem %s, ze zařízení, které používá tento klíč, " "budou odpovídajícím způsobem zvýrazněny v okně chatu." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Otisky klíčů se neshodují" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ověřte, zda porovnáváte správný otisk klíče. Pokud se otisky neshodují, účet " "%s může být ohrožen a měli byste zvážit zamítnutí tohoto klíče." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Ověření otisku klíče" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Porovnejte otisk tohoto klíče s otiskem zobrazeným na zařízení kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Odmítnout klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Zablokujte šifrovanou komunikaci se zařízením kontaktu, které tento klíč " "používá." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Přijmout klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Povolit šifrovanou komunikaci se zařízením kontaktu, které používá tento " "klíč." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Tento klíč je v současné době %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akceptován" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "To znamená, že jej %s může používat k přijímání a odesílání šifrovaných " "zpráv." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "ověřen" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Navíc bylo ověřeno, že odpovídá klíči v zařízení kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "odmítnut" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "To znamená, že jej %s nemůže použít k dešifrování zpráv a vy neuvidíte " "zprávy šifrované tímto klíčem." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Ze zařízení %s, které používá tento klíč, se šifrované zprávy nezobrazí. " "Toto zařízení už také nebude schopno rozluštit vaše zprávy." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Budete si moci vyměňovat šifrované zprávy se zařízením %s, které tento klíč " "používá." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zpět" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Spravovat" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Tento kontakt má nová zařízení" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Vyžaduje se rozhodnutí o důvěře OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Přidali jste nové zařízení pro účet %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Otisk vlastního klíče" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Budou generovány při prvním připojení" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Šifrování" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO zařízení" msgstr[1] "%d OMEMO zařízení" msgstr[2] "%d OMEMO zařízení" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO Správa klíčů" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automaticky přijímat nové klíče" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Nové šifrovací klíče od tohoto kontaktu budou automaticky přijímány." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Vlastní klíč" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nové klíče" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Přidružené klíče" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Neaktivní klíče" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nové šifrovací klíče z vašich ostatních zařízení budou automaticky přijímány." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Přijat" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Odmítnut" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Ověřen" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Nepoužit" dino-0.4.3/plugins/omemo/po/de.po0000644000000000000000000002731514452563620015327 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s hat ein nicht akzeptiertes Gerät benutzt. Du wirst Nachrichten von nicht " "akzeptierten Geräten nicht sehen." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Geräte verwalten" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s akzeptiert dieses Gerät nicht. Deshalb verpasst du vielleicht Nachrichten." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Schlüssel verwalten" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergleiche den Fingerabdruck, Zeichen für Zeichen, mit dem der auf dem Gerät " "deines Kontakts angezeigt wird." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingerabdrücke unterscheiden sich" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingerabdrücke stimmen überein" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Abbrechen" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bestätigen" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Schlüssel verifizieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Zukünftige Nachrichten, die von %s von dem Gerät gesendet werden, das diesen " "Schlüssel verwendet, werden im Chat-Fenster entsprechend hervorgehoben." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingerabdrücke stimmen nicht überein" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Bitte überprüfe, ob du den richtigen Fingerabdruck vergleichst. Wenn die " "Fingerabdrücke nicht übereinstimmen, ist das Konto von %s möglicherweise " "kompromittiert und du solltest in Betracht ziehen, den Schlüssel abzulehnen." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Fingerabdruck überprüfen" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Diesen Fingerabdruck mit dem Fingerabdruck vergleichen, der auf dem Gerät " "des Kontaktes angezeigt wird." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Schlüssel ablehnen" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockiert die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das " "diesen Schlüssel verwendet." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Schlüssel akzeptieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Erlaubt die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das " "diesen Schlüssel verwendet." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Dieser Schlüssel ist aktuell %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akzeptiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Das bedeutet, er kann von %s zum Empfangen und Senden verschlüsselter " "Nachrichten verwendet werden." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "überprüft" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Darüber hinaus wurde überprüft, dass er mit dem Schlüssel auf dem Gerät des " "Kontaktes übereinstimmt." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "abgelehnt" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Das bedeutet %s kann ihn nicht benutzen, um deine Nachrichten zu " "entschlüsseln, und du wirst damit verschlüsselte Nachrichten nicht sehen." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du wirst verschlüsselte Nachrichten von dem Gerät von %s, welches diesen " "Schlüssel benutzt nicht mehr sehen. Umgekehrt kann dieses Gerät deine " "Nachrichten nicht mehr entschlüsseln." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du wirst verschlüsselte Nachrichten mit dem Gerät von %s, welches diesen " "Schlüssel verwendet, austauschen können." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zurück" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Verwalten" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Dieser Kontakt hat neue Geräte" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-Vertrauensentscheidung erforderlich" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hast du ein neues Gerät für deinen Account %s hinzugefügt?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Eigener Fingerabdruck" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Wird beim ersten Verbinden erzeugt" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Verschlüsselung" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO Gerät" msgstr[1] "%d OMEMO Geräte" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO-Schlüssel Verwaltung" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Neue Schlüssel automatisch akzeptieren" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Neue Schlüssel von diesem Kontakt werden automatisch akzeptiert." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Eigener Schlüssel" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Neue Schlüssel" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Zugehörige Schlüssel" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inaktive Schlüssel" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Neue Schlüssel von deinen anderen Geräten werden automatisch akzeptiert." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Akzeptiert" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Zurückgewiesen" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verifiziert" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Unbenutzt" #~ msgid "Your contact" #~ msgstr "Dein Kontakt" #~ msgid "Not matching" #~ msgstr "Nicht übereinstimmend" #~ msgid "Matching" #~ msgstr "Stimmt überein" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Nach der Bestätigung werden alle mit diesem Schlüssel verschlüsselten " #~ "Nachrichten von %s hervorgehoben." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Diesen Schlüssel nicht mehr für die Kommunikation mit dem " #~ "Schlüsselbesitzer akzeptieren." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Akzeptiere diesen Schlüssel für die Kommunikation mit dem zugehörigen " #~ "Kontakt" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Das bedeutet, dass es von %s nicht verwendet werden kann, um Nachrichten " #~ "zu empfangen und alle von ihm gesendeten Nachrichten werden ignoriert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Nach der Bestätigung werden alle zukünftigen Nachrichten, die von %s mit " #~ "diesem Schlüssel gesendet werden, ignoriert und keine Ihrer Nachrichten " #~ "ist mit diesem Schlüssel lesbar." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Sobald bestätigt, kann %s diesen Schlüssel zum Senden und Empfangen von " #~ "Nachrichten verwenden." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Wenn dieser Kontakt neue Verschlüsselungscodes zu seinem Konto hinzufügt, " #~ "werden sie automatisch akzeptiert." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Wenn du einen neuen Schüssel zu deinem Account hinzufügst, wird dieser " #~ "automatisch akzeptiert." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Diesen Schlüssel für die Kommunikation mit dem entsprechenden Kontakt " #~ "akzeptieren." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Unbekanntes Gerät (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andere Geräte" #~ msgid "- None -" #~ msgstr "- Keine -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Datenbank Fehler" dino-0.4.3/plugins/omemo/po/dino-omemo.pot0000644000000000000000000001536314452563620017166 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "" msgstr[1] "" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "" dino-0.4.3/plugins/omemo/po/el.po0000644000000000000000000002652514452563620015341 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-04 09:55+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s χρησιμοποιεί μια μη αξιόπιστη συσκευή. Δεν θα βλέπετε μηνύματα από " "συσκευές που δεν εμπιστεύεστε." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Διαχείριση συσκευών" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s δεν εμπιστεύεται αυτήν τη συσκευή. Αυτό σημαίνει ότι μπορεί να χάσετε " "μηνύματα." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Διαχείριση Κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint), χαρακτήρα προς χαρακτήρα, " "με αυτό που εμφανίζεται στη συσκευή της επαφής σας." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) διαφέρουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) ταιριάζουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Ακύρωση" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Επιβεβαίωση" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Επαλήθευση κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Μελλοντικά μηνύματα που αποστέλλονται από %s από τη συσκευή που χρησιμοποιεί " "αυτό το κλειδί θα επισημαίνονται ανάλογα στο παράθυρο συνομιλίας." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) δεν ταιριάζουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Επαληθεύστε ότι συγκρίνετε το σωστό δακτυλικό αποτύπωμα (fingerprint). Εάν " "τα δακτυλικά αποτυπώματα (fingerprints) δεν ταιριάζουν, ο λογαριασμός του %s " "μπορεί να παραβιαστεί και θα πρέπει να σκεφτείτε να απορρίψετε αυτό το " "κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Επαλήθευση δακτυλικού αποτυπώματος (fingerprint) κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint) αυτού του κλειδιού με το " "δακτυλικό αποτύπωμα που εμφανίζεται στη συσκευή της επαφής." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Απόρριψη κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Αποκλεισμός κρυπτογραφημένης επικοινωνίας με τη συσκευή της επαφής που " "χρησιμοποιεί αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Αποδοχή κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Να επιτρέπεται η κρυπτογραφημένη επικοινωνία με τη συσκευή της επαφής που " "χρησιμοποιεί αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Αυτό το κλειδί είναι αυτή τη στιγμή %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "δεκτό" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Αυτό σημαίνει ότι μπορεί να χρησιμοποιηθεί από το %s για λήψη και αποστολή " "κρυπτογραφημένων μηνυμάτων." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "επαληθευμένο" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Επιπλέον, έχει επαληθευτεί ότι ταιριάζει με το κλειδί στη συσκευή της επαφής." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "απορριφθέν" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Αυτό σημαίνει ότι δεν μπορεί να χρησιμοποιηθεί από %s για την " "αποκρυπτογράφηση των μηνυμάτων σας και δεν θα βλέπετε μηνύματα " "κρυπτογραφημένα με αυτό." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Δεν θα βλέπετε κρυπτογραφημένα μηνύματα από τη συσκευή των %s που " "χρησιμοποιούν αυτό το κλειδί. Αντίθετα, αυτή η συσκευή δεν θα μπορεί πλέον " "να αποκρυπτογραφήσει τα μηνύματά σας." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Θα μπορείτε να ανταλλάσσετε κρυπτογραφημένα μηνύματα με τη συσκευή των %s " "που χρησιμοποιούν αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Πίσω" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Διαχείρηση" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Αυτή η επαφή έχει νέες συσκευές" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Απαιτείται απόφαση εμπιστοσύνης OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Προσθέσατε νέα συσκευή για τον λογαριασμό %s;" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Το δακτυλικό μου αποτύπωμα (fingerprint)" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Θα δημιουργηθεί με την πρώτη σύνδεση" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Κρυπτογράφηση" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO συσκευή" msgstr[1] "%d OMEMO συσκευές" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Διαχείριση κλειδιών OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Αυτόματη αποδοχή νέων κλειδιών" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Τα νέα κλειδιά κρυπτογράφησης από αυτήν την επαφή θα γίνονται δεκτά αυτόματα." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Το κλειδί μου" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Νέα κλειδιά" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Συσχετισμένα κλειδιά" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Ανενεργά κλειδιά" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Τα νέα κλειδιά κρυπτογράφησης από τις άλλες συσκευές σας θα γίνονται δεκτά " "αυτόματα." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Αποδεκτά" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Απορριφθέντα" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Επαληθευμένα" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Αχρησιμοποίητα" dino-0.4.3/plugins/omemo/po/en.po0000644000000000000000000001455714452563620015345 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "" msgstr[1] "" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "" dino-0.4.3/plugins/omemo/po/eo.po0000644000000000000000000002162614452563620015341 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-08 22:02+0000\n" "Language-Team: none\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5.1\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s uzas nefidatan aparaton. Vi ne vidos mesaĝojn el aparatoj, kiujn vi ne " "fidas." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Administri aparatojn" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s ne fidas ĉi tiun aparaton. Tio signifas, ke eble mankas al vi mesaĝoj." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Administri Ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Komparu la fingropremaĵon, signon post signo, kun la fingropremaĵo montrata " "sur la aparato de via kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingropremaĵoj diferencas" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingropremaĵoj kongruas" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Nuligi" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Konfirmi" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Konfirmi ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesaĝoj sendotaj de %s per la aparato kiu uzas ĉi tiun ŝlosilon estos " "emfazitaj laŭe en la babila fenestro." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingropremaĵoj ne kongruas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Bonvolu konfirmi, ke vi komparas la ĝustan fingropremaĵon. Se fingropremaĵoj " "ne kongruas, la konto de %s eble estas kompromitita, kaj vi konsideru " "rifuzon de ĉi tiu ŝlosilo." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Konfirmi ŝlosilan fingropremaĵon" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Komparu la fingropremaĵon de ĉi tiu ŝlosilo kun la fingropremaĵo montrata " "sur la aparato de la kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rifuzi ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Rifuzi ĉifritan komunikadon al la aparato de la kontakto, kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Akcepti ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Akcepti ĉifritan komunikadon al la aparato de la kontakto, kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ĉi tiu ŝlosilo estas aktuale %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akceptata" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Ĉi tio signifas, ke ĝi povas esti uzata de %s por ricevi kaj sendi ĉifritajn " "mesaĝojn." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "konfirmita" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Krome ĝi estis kontrolita pri kongruo kun la ŝlosilo ĉe la aparato de la " "kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rifuzita" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Tio signifas, ke ĝi ne povas esti uzata de %s por malĉifri viajn mesaĝojn, " "kaj vi ne vidos mesaĝojn ĉifritajn per ĝi." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Vi ne vidos ĉifritajn mesaĝojn el la aparato de %s kiu uzas ĉi tiun " "ŝlosilon. Male, tiu aparato ne plu povos malĉifri viajn mesaĝojn." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Vi povos interŝanĝi ĉifritajn mesaĝojn kun la aparato de %s kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Reen" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Administri" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ĉi tiu kontakto havas novajn aparatojn" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Necesas OMEMO-decido pri fidindeco" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ĉu vi aldonis novan aparaton por la konto %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Propra fingropremaĵo" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Estos farita dum unua konekto" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Ĉifrado" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO-aparato" msgstr[1] "%d OMEMO-aparatoj" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Administrado de OMEMO-Ŝlosiloj" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Aŭtomate akcepti novajn ŝlosilojn" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Novaj ĉifraj ŝlosiloj de ĉi tiu kontakto estos aŭtomate akceptitaj." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Propra ŝlosilo" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Novaj ŝlosiloj" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Asociitaj ŝlosiloj" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Neaktivaj ŝlosiloj" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novaj ĉifraj ŝlosiloj de viaj aliaj aparatoj estos aŭtomate akceptitaj." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Akceptita" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rifuzita" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Konfirmita" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Ne uzata" dino-0.4.3/plugins/omemo/po/es.po0000644000000000000000000002704714452563620015350 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-16 22:33+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s ha estado usando un dispositivo poco fiable. No verá mensajes de " "dispositivos en los que no confíe." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gestionar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s no confía en este dispositivo. Eso significa que puede que te falten " "mensajes." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestionar Clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compara la huella digital, carácter a carácter, con la huella digital " "mostrada en el dispositivo de tus contactos." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Las huellas digitales son diferente" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Las huellas digitales son idénticas" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Los mensajes futuros enviados por %s desde el dispositivo que usa esta clave " "será resaltado en consecuencia en esta venta de conversación." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Huellas digitales no coinciden" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor, verifica que estás comparando la huella digital correcta. Si la " "huella digital no coincide con la cuenta %s puede que la clave esté " "comprometida y deberías considerar rechazar esta clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar Huella Digital" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compara esta huella digital con la huella digital mostrada en el dispositivo " "de tu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rechazar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquear comunicación cifrada con el dispositivo del contacto que usa esta " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Aceptar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permitir comunicación cifrada con el dispositivo del contacto que usa esta " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Esta clave está actualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Esto significa que puede ser usada por %s para recibir y enviar mensajes " "cifrados." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verficada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Además, ha sido verificado que las claves coinciden con la clave del " "dispositivo de tu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rechazada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Esto significa que no puede ser usada por %s para descifrar tus mensajes, y " "tú no verás los mensajes cifrados con ella." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "No verás los mensajes cifrados desde el dispositivo de %s que usa esta " "clave. A la inversa, ese dispositivo ya no podrá descifrar tus mensajes." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Tu podrás intercambiar mensajes cifrados con el dispositivo de %s que usa " "esta clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Volver" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestionar" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Este contacto tiene un nuevo dispositivo" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisión de confianza requerida para clave OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "¿Añadiste un nuevo dispositivo para la cuenta %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Tu huella digital" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Será generada en la primera conexión" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Cifrado" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispositivo OMEMO" msgstr[1] "%d dispositivos OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Huellas digitales OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automáticamente aceptar nuevas claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Las nuevas claves de cifrados para este contacto serán aceptadas " "automáticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Mi clave" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nuevas claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Claves asociadas" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Claves inactivas" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Las nuevas claves de cifrado de tus otros dispositivos serán aceptadas " "automáticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Aceptada" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rechazada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Sin usar" #~ msgid "Your contact" #~ msgstr "Tu contacto" #~ msgid "Not matching" #~ msgstr "No coincide" #~ msgid "Matching" #~ msgstr "Coincide" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Una vez confirmada, cualquier futuro mensaje enviado por %s usando esta " #~ "clave será resaltado en el ventana de conversación." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dejar de aceptar esta clave durante la comunicación con este contacto." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Aceptar esta clave durante la comunicación con este contacto" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Esto significa que no puede ser usada por %s para recibir mensajes, y " #~ "cualquier mensaje enviado con esta clave será ignorado." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Una vez confirmado, cualquier futuro mensaje enviado por %s usando esta " #~ "clave será ignorado y ninguno de tus mensajes será legible usando esta " #~ "clave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Una vez confirmada esta clave será usable por %s para recibir y enviar " #~ "mensajes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Cuando este contacto añada nuevas claves de cifrado a su cuenta, " #~ "automáticamente aceptarlas." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Cuando añades nuevas claves de cifrado a tu cuenta, aceptarlas " #~ "automáticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "Aceptar esta clave durante la comunicación con este contacto." #~ msgid "Reject Key" #~ msgstr "Rechazar clave" #~ msgid "Accept Key" #~ msgstr "Aceptar clave" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo (0x%.8x) desconocido" #~ msgid "Other devices" #~ msgstr "Otros dispositivos" #~ msgid "- None -" #~ msgstr "- Ninguno -" #~ msgid "OMEMO" #~ msgstr "OMEMO" dino-0.4.3/plugins/omemo/po/eu.po0000644000000000000000000002672614452563620015355 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-01-17 17:56+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s erabiltzaileak fidagarritasun gutxiko gailu bat erabiltzen egon da. Ez " "duzu mezurik ikusiko konfiantza ematen ez dizuten gailuengandik." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gailuak kudeatu" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s erabiltzailea ez da gailu honetaz fidatzen. Honek mezu batzuk gal " "ditzakezula esan nahi du." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gakoa kudeatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Hatz marka konparatu, hizkiz hizki, zure kontaktuaren gailuan ageri " "denarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Hatz markak ez datoz bat" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Hatz markak bat datoz" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Utzi" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Baieztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Gakoa egiaztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "%s erabiltzaileak etorkizunean gako hau erabiltzen duen gailutik bidalitako " "mezuak nabarmenduko dira txat leihoan." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Hatz markak ez datoz bat" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Mesedez egiaztatu ezazu hatz marka egokia konparatzen ari zarela. Hatz " "markak bat ez badatoz baliteke %s(e)ren kontua arriskuan egotea eta gako hau " "ez onartzea kontuan hartu beharko zenuke." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Gakoaren hatz marka egiaztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Konpara ezazu gako honen hatz marka kontaktuaren gailuan agertzen den hatz " "markarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Gakoa ukatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Enkriptatutako komunikazioa blokeatu gako hau erabiltzen duen kontaktuaren " "gailuarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Gakoa onartu" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Enkriptatutako komunikazioa baimendu gako hau erabiltzen duen kontaktuaren " "gailuarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Gako hau %s dago une honetan." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "onartuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Horrek esan nahi du %s-ek erabil dezakeela mezu zifratuak jasotzeko eta " "bidaltzeko." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "egiaztatuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Gainera zure kontaktuaren gailuan ageri den gakoarekin bat datorrela " "egiaztatu da." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ukatuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Honek esan nahi du %s erabiltzaileak ezin duela erabili zure mezuak " "deszifratzeko, eta zuk ez dituzula gako honekin enkriptatutako mezuak " "ikusiko." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Ez dituzu ikusiko gako hau erabiltzen duen %s kontaktuaren gailutik " "igorritako mezuak. Era berean, hemendik aurrera gailu horrek ez da zure " "mezuak deszifratzeko gai izango." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Gako hau erabiltzen duen %s kontaktuaren gailuarekin enkriptatutako mezuak " "elkar-trukatzeko gai izango zara." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atzera" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Kudeatu" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Kontaktu honek gailu berriak ditu" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO konfiantza erabakia behar da" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Gailu berri gehitu duzu %s konturako?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Norberaren hatz marka" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Lehen konexioan sortuko da" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Enkriptazioa" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "OMEMO gailu %d" msgstr[1] "%d OMEMO gailu" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO gakoen kudeaketa" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Gako berriak automatikoki onartu" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Kontaktu honen enkriptatze gako berriak onartuak izango dira automatikoki." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Norberaren gakoa" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Gako berriak" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Lotutako gakoak" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Gako ez aktiboak" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Zure beste gailuen enkriptatze gako berriak onartuak izango dira " "automatikoki." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Onartuta" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Ukatuta" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Egiaztatuta" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Ez erabilita" #~ msgid "Not matching" #~ msgstr "Ez datoz bat" #~ msgid "Matching" #~ msgstr "Bat datoz" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Egiaztatu ondoren, %s(e)k gako honekin etorkizunean bidalitako mezu " #~ "guztiak behar bezala nabarmenduko dira txat leihoan." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Utzi gako hau onartzeari lotuta dagoen kontaktuarekin hitz egiten duzun " #~ "bitartean." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Hasi gako hau onartzen lotuta dagoen kontaktuarekin hitz egiten duzun " #~ "bitartean" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Honek esan nahi du %s(e)k ezin duela gakoa mezuak jasotzeko erabili, eta " #~ "berak bidalitako edozein mezu alde batera utziko da." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Egiaztatu ondoren, etorkizunean %s(e)k gako hau erabiliz bidalitako " #~ "edozein mezu alde batera utziko da eta zure mezuetako bat ere ez da " #~ "irakurgarria izango gako hau erabiliz." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Egiaztatu ondoren %s(e)k gako hau mezuak jaso eta bidaltzeko erabili ahal " #~ "izango du." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Kontaktu honek enkriptatzeko gako berriak bere kontura gehitzen " #~ "dituenean, automatikoki onartu." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Zure kontura enkriptazio gako berriak gehitzen dituzunean, automatikoki " #~ "onartu." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Hasi gako hau onartzen lotuta dagoen kontaktuarekin hitz giten duzun " #~ "bitartean." #~ msgid "Reject Key" #~ msgstr "Gakoa baztertu" #~ msgid "Accept Key" #~ msgstr "Gakoa onartu" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Gailu ezezaguna (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Beste gailuak" #~ msgid "- None -" #~ msgstr "- Bat ere ez -" dino-0.4.3/plugins/omemo/po/fa.po0000644000000000000000000002443114452563620015321 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-08-10 03:32+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s از دستگاهی استفاده می‌کند که اعتبارش را تأیید نکرده‌اید، پیام‌ها از چنین " "دستگاه‌هایی قابل مشاهده نیستند." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "مدیریت دستگاه‌ها" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s اعتبار این دستگاه را تأیید نکرده است. این یعنی، ممکن است پیام‌هایی را " "دریافت نکنید/نکرده باشید." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "مدیریت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "اثرانگشت را با آنی که در دستگاه مخاطبتان است مقایسه کنید، کاراکتر به کاراکتر." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "اثرانگشت‌ها متفاوت‌اند" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "اثرانگشت‌ها تطابق دارند" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "لغو" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "تأیید" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "تأیید صحت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "پیام‌های ارسالی توسط %s از دستگاهی که از این کلید استفاده می‌کند، مطابق آن در " "پنجرهٔ گپ مشخص می‌شوند." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "اثرانگشت‌ها تطابق ندارند" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "لطفاً مطمئن شوید که در حال مقایسهٔ اثرانگشت صحیح هستید. اگه اثرانگشت‌ها تطابق " "نداشته باشند، ممکن است اطلاعات حساب %s لو رفته باشد و باید رد کردن این کلید " "را در نظر بگیرید." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "تأیید صحت اثرانگشت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "اثرانگشت این کلید را با اثرانگشت نمایش‌داده‌شده در دستگاه مخاطب مقایسه کنید." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "رد کردن کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "ارتباط رمزگذاری‌شده را با دستگاهی که از این کلید استفاده می‌کند، مسدود کنید." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "پذیرش کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "اجازهٔ ارتباط رمزگذاری‌شده را با دستگاهی که از این کلید استفاده می‌کند، بدهید." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "این کلید در حال حاضر %s است." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "پذیرفته شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "این یعنی %s می‌تواند از آن برای ارسال و دریافت پیام رمزگذاری‌شده استفاده کند." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "تأیید صحت شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "به علاوه تأیید شده که با کلید دستگاه مخاطب تطابق دارد." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "رد شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "این یعنی %s نمی‌تواند با آن پیام‌های شما را رمزگشایی کند، و شما پیام‌های " "رمزگذاری‌شده با آن را نخواهید دید." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "شما پیام‌های رمزگذاری‌شده را از آن دستگاهِ %s که از این کلید استفاده می‌کند، " "نخواهید دید. از سوی دیگر، آن دستگاه نیز پس از این قادر به رمزگشایی پیام‌های " "شما نخواهد بود." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "شما قادر به تبادل پیام‌های رمزگذاری‌شده با آن دستگاهِ %s که از این کلید استفاده " "می‌کند، هستید." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "بازگشت" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "مدیریت" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "این مخاطب دستگاه‌های جدیدی دارد" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "تصمیم اعتبار اُمیمو لازم است" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "آیا برای حساب %s یک دستگاه جدید اضافه کرده‌اید؟" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "اثرانگشت خود" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "در اولین اتصال تولید خواهد شد" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "رمزگذاری" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d دستگاه OMEMO" msgstr[1] "%d دستگاه OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "مدیریت کلید اُمیمو" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "پذیرش خودکار کلیدهای جدید" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "کلیدهای جدید این مخاطب به طور خودکار پذیرفته می‌شوند." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "کلید خود" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "کلیدهای جدید" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "کلیدهای مرتبط" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "کلیدهای غیرفعال" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "کلیدهای رمزگذاری جدید از دستگاه‌های دیگرتان به طور خودکار پذیرفته می‌شوند." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "پذیرفته شده" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "رد شده" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "تأیید صحت شده" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "استفاده نشده" dino-0.4.3/plugins/omemo/po/fi.po0000644000000000000000000001532114452563620015327 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-12 20:01+0000\n" "Language-Team: none\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Peruuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Takaisin" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "" msgstr[1] "" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "" dino-0.4.3/plugins/omemo/po/fr.po0000644000000000000000000002701114452563620015337 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-12 17:21+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilisé un client inconnu. Vous ne verrez pas les messages provenant de " "clients en lesquels vous n’avez pas confiance." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gérer les clients" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s n’accorde pas sa confiance à ce client. Cela signifie que vous manquez " "peut-être des messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gérer la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparez l’empreinte, lettre par lettre, avec celle affichée sur le client " "de votre contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "L’empreinte ne correspond pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "L’empreinte correspond" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annuler" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmer" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Vérifier la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Les prochains messages envoyés par %s depuis le client utilisant cette clé " "seront mis en valeur dans la fenêtre de chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Les empreintes ne correspondent pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Veuillez vérifier que vous comparez la bonne empreinte. Si les empreintes ne " "correspondent pas, le compte de %s pourrait être compromis et vous devriez " "envisager de rejeter cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Vérifier l’empreinte de cette clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "Comparez cette empreinte avec celle affichée sur le client du contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rejeter cette clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquer les communications chiffrées avec le client du contact qui utilise " "cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Accepter la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Autoriser les communications chiffrées avec le client du contact qui utilise " "cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Cette clé est actuellement %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptée" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Cela signifie qu’elle peut être utilisée par %s pour recevoir et envoyer des " "messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "vérifiée" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "De plus, vous avez précédemment vérifié que cette clé coïncide avec celle du " "client de ce contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejetée" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Cela signifie qu’elle ne peut pas être utilisée par %s pour déchiffrer vos " "messages, et que vous ne verrez pas les messages chiffrés avec." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Vous ne verrez pas les messages de %s provenant du client qui utilise cette " "clé. En retour, ce client ne pourra plus déchiffrer vos messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Vous pourrez échanger des messages chiffrés avec le client de %s qui utilise " "cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Retour" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gérer" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ce contact a de nouveaux clients" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Décision de confiance OMEMO requise" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Avez-vous ajouté un nouveau client pour le compte %s ?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Empreinte publique" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Sera générée lors de la première connexion" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Chiffrement" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d client OMEMO" msgstr[1] "%d clients OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gestion des clés OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Accepter automatiquement les nouvelles clés" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Les nouvelles clés de chiffrement de ce contact seront acceptées " "automatiquement." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Clé publique personnelle" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nouvelles clés" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Clés associées" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Clés inactives" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Les nouvelles clés de chiffrement de vos autres clients seront acceptées " "automatiquement." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Acceptée" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rejetée" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Vérifiée" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Inutilisée" #~ msgid "Your contact" #~ msgstr "Votre contact" #~ msgid "Not matching" #~ msgstr "Ne correspond pas" #~ msgid "Matching" #~ msgstr "Correspond" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Après confirmation, tout futur message envoyé par %s utilisant cette clé " #~ "sera surligné dans la fenêtre de conversation." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Arrêter d'accepter cette clé durant la communication avec son contact " #~ "associé." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Accepter cette clé durant la communication avec son contact associé" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Cela signifie qu'elle ne peut pas être utilisée par %s pour recevoir des " #~ "messages, et que les messages envoyés seront ignorés." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Après confirmation, tout futur message envoyé par %s utilisant cette clé " #~ "sera ignoré et vos messages ne seront pas lisibles en utilisant cette clé." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Après confirmation, cette clé sera utilisable par %s pour recevoir et " #~ "envoyer des messages." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quand ce contact ajoute une nouvelle clé de chiffrement à son compte, " #~ "l'accepter automatiquement." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Accepter automatiquement les nouvelles clés de chiffrements que vous " #~ "ajoutez à votre compte." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepter cette clé durant la communication avec son contact associé." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Périphérique inconnu (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Autres périphériques" #~ msgid "- None -" #~ msgstr "- Aucun -" dino-0.4.3/plugins/omemo/po/gl.po0000644000000000000000000002662414452563620015343 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-28 07:12+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.12-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s estivo utilizando un dispositivo non verificado. Non verás mensaxes " "procedentes de dispositivos nos que non confiaches." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Xestionar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s non mostrou confianza neste dispositivo. Porén, poderías botar a faltar " "algunha mensaxe." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Xestionar Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparar impresión dixital, caracter a caracter, coa mostrada no dispositivo " "do teu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impresións dixitais son diferentes" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impresións coinciden" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "As mensaxes futuras enviadas por %s desde o dispositivo que usa esta chave " "será resaltadas na ventá da conversa." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Non coinciden as impresións dixitais" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Comproba que estás comparando a impresión correcta. Se as impresións " "dixitais non coinciden, a conta de %s podería estar comprometida e deberías " "considerar rexeitar esta chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar a impresión dixital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compara a impresión dixital desta chave coa impresión mostrada no " "dispositivo do contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rexeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquear a comunicación cifrada co dispositivo do contacto que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Aceptar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permitir a comunicación cifrada co dispositivo do contacto que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Esta chave está actualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Esto significa que pode ser utilizada por %s para recibir e enviar mensaxes " "cifradas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verficada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Adicionalmente foi verificada e coincide coa chave no dispositivo do " "contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rexeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Esto significa que non pode ser usada por %s para descifrar as túas " "mensaxes, e non verás as mensaxes cifradas con ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Non verás as mensaxes cifradas desde o dispositivo de %s que usa esta chave. " "De xeito recíproco, ese dispositivo non poderá descifrar as túas." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Poderás intercambiar mensaxes cifradas co dispositivo de %s que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atrás" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Xestionar" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Este contacto ten novos dispositivos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Precisa decidir sobre a confianza OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Engadiches un novo dispositivo para a conta %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Impresión dixital propia" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Crearase na primeira conexión" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Cifrado" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispositivo OMEMO" msgstr[1] "%d dispositivos OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Xestión das chaves OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Aceptar automáticamente novas chaves" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Serán aceptadas de xeito automático novas chaves de cifrado de este contacto." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Chave propia" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Novas chaves" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Chaves asociadas" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Chaves non activas" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Aceptaranse de xeito automático novas chaves de cifrado do teus dispositivos." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Aceptada" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rexeitada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Sen usar" #~ msgid "Your contact" #~ msgstr "O teu contacto" #~ msgid "Not matching" #~ msgstr "Non coinciden" #~ msgid "Matching" #~ msgstr "Coinciden" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Unha vez confirmada, as futuras mensaxes enviadas por %s utilizando esta " #~ "chave será resaltada acorde na ventá de conversa." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Deixar de aceptar esta chave durante a comunicación co contacto asociado " #~ "a ela." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Comezar aceptando esta chave durante a comunicación co seu contacto " #~ "asociado" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Esto significa que non pode ser utilizada por %s para recibir mensaxes, e " #~ "calquera mensaxe enviada con ela será ignorada." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Unha vez confirmada, calquera mensaxe futura enviada por %s utilizando " #~ "esta chave será ignorada e ningunha das súas mensaxes serán lexibles " #~ "usando esta chave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Unha vez confirmada esta chave será utilizable por %s para recibir e " #~ "enviar mensaxes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Cando este contacto engade novas chaves de cifrado a súa conta, aceptalas " #~ "automáticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Cando engades novas chaves de cifrado a túa conta, aceptalas " #~ "automáticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Comezar a aceptar esta chave nas comunicacións co contacto asociado a ela." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo descoñecido (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Outros dispositivos" #~ msgid "- None -" #~ msgstr "-Nada-" dino-0.4.3/plugins/omemo/po/hu.po0000644000000000000000000002262014452563620015345 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-12-07 00:53+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.10-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s nem megbízható eszközt használt. Nem fogja látni az olyan eszközökről " "érkező üzeneteket, amelyekben nem bízik meg." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Eszközök kezelése" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nem bízik meg ebben az eszközben. Ez azt jelenti, hogy lemaradhat néhány " "üzenetről." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Kulcs kezelése" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Hasonlítsa össze az ujjlenyomatot karakterről karakterre a partnere eszközén " "láthatóval." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Az ujjlenyomatok eltérnek" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Az ujjlenyomatok egyeznek" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Mégse" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Megerősítés" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Kulcs ellenőrzése" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "%s által az ezt a kulcsot használó eszközről küldött jövőbeli üzenetek ennek " "megfelelően lesznek kiemelve a csevegésablakban." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Az ujjlenyomatok nem egyeznek" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ellenőrizze, hogy a megfelelő ujjlenyomatot hasonlítja-e össze. Ha az " "ujjlenyomatok nem egyeznek, akkor %s fiókja veszélybe kerülhetett, és " "fontolóra kellene vennie a kulcs elutasítását." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Kulcs ujjlenyomatának ellenőrzése" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Hasonlítsa össze ennek a kulcsnak az ujjlenyomatát a partner eszközén " "megjelenített ujjlenyomattal." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Kulcs elutasítása" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "A titkosított kommunikáció tiltása a partner eszközével, amely ezt a kulcsot " "használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Kulcs elfogadása" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "A titkosított kommunikáció engedélyezése a partner eszközével, amely ezt a " "kulcsot használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ez a kulcs jelenleg %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "elfogadott" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Ez azt jelenti, hogy %s használhatja titkosított üzenetek fogadásához és " "küldéséhez." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "ellenőrzött" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Ezenkívül ellenőrizve lett, hogy a kulcs egyezik a partner eszközén." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "elutasított" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Ez azt jelenti, hogy %s nem használhatja az Ön üzeneteinek visszafejtésére, " "és Ön nem fogja látni az azzal titkosított üzeneteket." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nem fogja látni %s azon eszközéről érkező titkosított üzeneteket, amely ezt " "a kulcsot használja. Fordítva, a másik eszköz nem lesz képes többé " "visszafejteni az Ön üzeneteit." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Ön képes lesz titkosított üzeneteket váltani %s azon eszközével, amely ezt a " "kulcsot használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Vissza" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Kezelés" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ennek a partnernek új eszközei vannak" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO megbízhatósági döntés szükséges" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hozzáadott egy új eszközt a(z) %s fiókhoz?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Saját ujjlenyomat" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Az első kapcsolódáskor lesz előállítva" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Titkosítás" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO eszköz" msgstr[1] "%d OMEMO eszköz" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO kulcskezelés" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Új kulcsok automatikus elfogadása" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Az ettől a partnertől származó új titkosítási kulcsok automatikusan el " "lesznek fogadva." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Saját kulcs" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Új kulcsok" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Hozzárendelt kulcsok" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inaktív kulcsok" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Az egyéb eszközeitől származó új titkosítási kulcsok automatikusan el " "lesznek fogadva." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Elfogadva" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Elutasítva" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Ellenőrizve" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Nem használt" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Ismeretlen eszköz (0x%.8x)" #~ msgid "Other devices" #~ msgstr "További eszközök" #~ msgid "- None -" #~ msgstr "- Nincs -" dino-0.4.3/plugins/omemo/po/id.po0000644000000000000000000002142614452563620015330 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-01-10 15:32+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s telah menggunakan perangkat tidak dikenal. Anda tidak akan melihat pesan " "dari perangkat yang tidak Anda kenal." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Atur perangkat" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s tidak mengenali perangkat ini. Artinya, Anda mungkin kehilangan pesan." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Atur kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Bandingkan sidik jari karakter demi karakter, dengan yang tampil di " "perangkat kontak Anda." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Sidik jari berbeda" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Sidik jari cocok" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Batal" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Konfirmasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifikasi kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Pesan mendatang yang dikirim oleh %s dari perangkat yang menggunakan kunci " "ini akan ditandai di halaman percakapan." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Sidik jari tidak cocok" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Harap Anda memverifikasi sidik jari dengan benar. Jika sidik jari tidak " "cocok, akun %s mungkin telah disusupi dan pertimbangkan untuk menolak kunci " "ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifikasi sidik jari kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Bandingkan sidik jari kunci ini dengan sidik jari yang ditampilkan perangkat " "kontak." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Tolak kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokir komunikasi terenkripsi dengan perangkat kontak yang menggunakan kunci " "ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Terima kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Izinkan komunikasi terenkripsi dengan perangkat kontak yang menggunakan " "kunci ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Status kunci saat ini %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "diterima" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Artinya dapat digunakan oleh %s untuk menerima dan mengirim pesan " "terenkripsi." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "Terverifikasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Telah terverifikasi dengan mencocokkan kunci pada perangkat kontak." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ditolak" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Artinya tidak dapat digunakan oleh %s untuk menguraikan pesan Anda, dan Anda " "tidak akan melihat pesan dienkripsi dengannya." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Anda tidak akan melihat pesan terenkripsi dari perangkat %s yang menggunakan " "kunci ini. Sebaliknya, perangkat itu tidak dapat lagi menguraikan pesan Anda." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Anda dapat bertukar pesan terenkripsi dengan perangkat %s yang menggunakan " "kunci ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Kembali" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Pengaturan" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Kontak memiliki perangkat baru" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Memerlukan persetujuan atas OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Apakah Anda menambahkan perangkat baru untuk akun %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Sidik jari sendiri" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Akan dibuat pada koneksi pertama" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Enkripsi" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO perangkat" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Pengaturan kunci OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Otomatis mengkonfirmasi kunci baru" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Kunci enkripsi baru dari kontak ini akan diterima secara otomatis." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Kunci milik sendiri" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Kunci baru" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Kunci terkait" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Kunci non-aktif" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Kunci enkripsi baru dari perangkat Anda yang lain akan diterima secara " "otomatis." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Diterima" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Ditolak" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Terverifikasi" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Tidak terpakai" #~ msgid "Your contact" #~ msgstr "Kontak anda" dino-0.4.3/plugins/omemo/po/ie.po0000644000000000000000000002427214452563620015333 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-01 10:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gerer aparates" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s ne have fide por ti-ci aparate. Vu fórsan manca alcun missages." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerer li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Cuidosi compara li fingre-print con ti que es monstrat sur li aparate de vor " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingre-printes difere" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingre-printes es identic" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anullar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingre-printes ne es egal" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ples verificar que vu compara li just fingre-print. Si fingre-printes ne es " "egal, li conto de %s posse esser compromettet e vu deve rejecter ti-ci clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar li fingre-print" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparar li fingre-print de ti-ci clave con clave que es monstrat sur li " "aparate del contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rejecter li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocar ciffrat communication con li aparate del contacte quel usa ti-ci " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Acceptar li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permisser ciffrat communication con li aparate del contacte quel usa ti-ci " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ti-ci clave es actualmen %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptat" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "To significa que %s posse usar it por reciver e inviar missages." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificat" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Adplu, it ha esset verificat corresponder con li clave sur li aparate del " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejectet" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "To significa que %s posse usar it por deciffrar vor missages e que vu ne va " "vider li missages ciffrat med it." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Retro" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerer" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ti-ci contacte have nov aparates" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Un decision de confidentie OMEMO es besonat" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Esque vu ha adjuntet un nov aparate por li conto %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Propri fingre-print" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Va esser generat pos li prim conexion" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Ciffration" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d aparate OMEMO" msgstr[1] "%d aparates OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gerentie de claves OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automaticmen acceptar nov claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Nov claves de ciffration de ti-ci contacte va esser acceptat automaticmen." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Propri clave" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nov claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Associat claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Ínactiv claves" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nov claves de ciffration de vor altri aparates va esser acceptat " "automaticmen." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Acceptat" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rejectet" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificat" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Ínusat" #~ msgid "Not matching" #~ msgstr "Ne corresponde" #~ msgid "Matching" #~ msgstr "Corresponde" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Pos li confirmation, omni futuri missages inviat per %s con ti-ci clave " #~ "va esser colorat correspondmen in li fenestre de conversation." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Cessar acceptar ti-ci clave por communication con su associat contacte." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Acceptar ti-ci clave por communication con su associat contacte" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "To significa que %s ne posse usar it por reciver missages, e su missages " #~ "inviat va esser ignorat." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Pos confirmation, omni futuri missages inviat per %s con ti-ci clave va " #~ "esser ignorat, e null vor missages va esser leibil con ti-ci clave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Pos confirmation, ti-ci clave va esser usabil per %s por reciver e inviar " #~ "missages." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quande ti-ci contacte adjunte nov ciffre-claves a su conto, acceptar les " #~ "automaticmen." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quande vu adjunte nov ciffre-claves a vor conto, acceptar les " #~ "automaticmen." dino-0.4.3/plugins/omemo/po/it.po0000644000000000000000000002725414452563620015355 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-25 11:28+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s sta usando un dispositivo non verificato. Non vedrai messaggi inviati da " "un dispositivo che non hai verificato." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gestisci dispositivi" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s non ha verificato questo dispositivo. Ciò significa che potresti perdere " "dei messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestisci la chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Confronta la fingerprint, carattere per carattere, con quella mostrata sul " "dispositivo del tuo contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Le impronte non combaciano" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Le impronte combaciano" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annulla" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Conferma" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifica la chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "I messaggi futuri inviati da %s dal dispositivo che usa questa chiave " "saranno evidenziati di conseguenza nella finestra della chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Le impronte non corrispondono" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Si prega di verificare che si stia confrontanto la fingerprint corretta. Se " "le fingerprint non corrispondono, l'account di %s potrebbe essere " "compromesso e si dovrebbe rifiutare questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifica la fingerprint della chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Confronta la fingerprint di questa chiave con quella mostrata sul " "dispositivo del contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Chiave rifiutata" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocca le comunicazioni crittografate con il dispositivo del contatto che " "sta usando questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Chiave accettata" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permetti la comunicazione crittografata con il dispositivo del contatto che " "sta usando questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "La chiave attualmente è %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "accettata" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Questo vuol dire che può essere usata da %s per ricevere ed inviare messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificata" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Inoltre è stato verificato che corrisponda con la chiave sul dispositivo del " "contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rifiutata" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Questo vuol dire che non può essere usata da %s per decifrare i tuoi " "messaggi, e tu non vedrai i messaggi crittografati con essa." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Non vedrai i messaggi crittografati dal dispositivo di %s che usa questa " "chiave. Al contrario, quel dispositivo non sarà più in grado di decifrare i " "tuoi messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Sarai in grado di scambiare messaggi crittografati con il dispositivo di %s " "che usa questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Indietro" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestisci" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Questo contatto ha dei nuovi dispositivi" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Una decisione sulla fiducia è necessaria per la chiave OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hai aggiunto un nuovo dispositivo per l'account %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Propria impronta" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Verrà generata alla prima connessione" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Crittografia" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispositivo OMEMO" msgstr[1] "%d dispositivi OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gestione delle chiavi OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Accetta automaticamente le nuove chiavi" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Le nuove chiavi di crittografia di questo contatto verranno accettate " "automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Proprie chiavi" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nuove chiavi" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Chiavi associate" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Chiavi inattive" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Le nuove chiavi di crittografia dei tuoi altri dispositivi verranno " "accettate automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Accettata" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rifiutata" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificata" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Inutilizzata" #~ msgid "Your contact" #~ msgstr "Il tuo contatto" #~ msgid "Not matching" #~ msgstr "Non corrispondenti" #~ msgid "Matching" #~ msgstr "Corrispondenti" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Una volta confermato, qualsiasi messaggio futuro inviato da %s usando " #~ "questa chiave sarà evidenziato di conseguenza nella finestra della " #~ "conversazione." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Smetti di accettare questa chiave durante le comunicazioni col contatto " #~ "associato." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Inizia ad accettare questa chiave durante le comunicazioni con il " #~ "contatto ad essa associato" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Questo vuol dire che non può essere usata da %s per ricevere messaggi, ed " #~ "ogni messaggio inviato attraverso di essa sarà ignorato." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Una volta confermato, ogni messaggio futuro inviato da %s usando questa " #~ "chiave sarà ignorato e nessuno dei tuoi messaggi sarà leggibile usando " #~ "questa chiave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Una volta confermata questa chiave sarà utilizzabile da %s per ricevere " #~ "ed inviare messaggi." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quando questo contatto aggiunge nuove chiavi di cifratura al proprio " #~ "account, accettale automaticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quando aggiungi nuove chiavi di cifratura al tuo account, accettale " #~ "automaticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Inizia ad accettare questa chiave durante le comunicazioni col contatto " #~ "associato" #~ msgid "Reject Key" #~ msgstr "Rifiuta la chiave" #~ msgid "Accept Key" #~ msgstr "Accetta la chiave" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo sconosciuto (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Altri dispositivi" #~ msgid "- None -" #~ msgstr "- Nessuno -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Errore del database" dino-0.4.3/plugins/omemo/po/ja.po0000644000000000000000000002766314452563620015337 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: none\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s は信頼されていないデバイスを使用しています。信頼していないデバイスから送信" "されたメッセージは閲覧できません。" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "デバイスを管理" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s はこのデバイスを信頼していません。相手がメッセージを正しく受信できない可能" "性があります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "キーを管理" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "以下のフィンガープリントと、お使いのデバイスに表示されているフィンガープリン" "トを、1文字ずつ比較してください。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "フィンガープリントが一致しません" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "フィンガープリントが一致します" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "キャンセル" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "確認" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "キーを確認" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "今後このキーを使ったデバイスから %s が送信するメッセージは、チャットウィンド" "ウでハイライト表示されます。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "フィンガープリントが一致しません" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "正しいフィンガープリントを比較しているか確認してください。フィンガープリント" "が一致しない場合、%s のアカウントはなりすましの可能性があるので、このキーを拒" "否することをおすすめします。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "キーのフィンガープリントを検証" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "このキーのフィンガープリントと、連絡先のデバイスに表示されたフィンガープリン" "トを比較します。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "キーを拒否" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "このキーを使った連絡先のデバイスとの暗号化された会話をブロックします。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "キーを許可" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "このキーを使った連絡先のデバイスとの暗号化された会話を許可します。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "このキーは現在%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "許可されています" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "このキーを使って、%s は暗号化されたメッセージを送受信できます。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "検証済みです" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "加えて、連絡先のデバイスのキーと一致することを検証済みです。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "拒否されています" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "このキーを使って、%s はあなたのメッセージを解読できなくなります。また、あなた" "は、このキーで暗号化されたメッセージを確認できなくなります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "このキーを使った %s のデバイスから、暗号化されたメッセージを確認できなくなり" "ます。逆に、今後そのデバイスでは、あなたのメッセージを解読できなくなります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "このキーを使った %s のデバイスと、暗号化されたメッセージをやり取りできるよう" "になります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "戻る" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "この連絡先に新しいデバイスが追加されました" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO 信頼選択が必要です" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "アカウント %s に新しいデバイスを追加しましたか?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "自身のフィンガープリント" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "最初の接続で生成されます" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "暗号化" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d 台の OMEMO デバイス" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO キー管理" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "自動的に新しいキーを許可" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "この連絡先から発行された新しい暗号化キーが自動的に許可されます。" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "自身のキー" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "新しいキー" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "関連付けられたキー" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "非アクティブなキー" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "あなたのほかのデバイスから発行された新しい暗号化キーが自動的に許可されます。" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "許可" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "拒否" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "検証済み" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "未使用" #~ msgid "Your contact" #~ msgstr "あなたの連絡先" #~ msgid "Not matching" #~ msgstr "一致しない" #~ msgid "Matching" #~ msgstr "一致" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一度確認すると、今後このキーを使って %s から送信されるメッセージは、チャッ" #~ "トウィンドウでハイライト表示されます。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "関連付けられた連絡先とトークをする際に、このキーを許可しないようにします。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "関連付けられた連絡先とトークをする際に、このキーを許可するようにします" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "このキーを使って %s からのメッセージを受信できません。また、当該アドレスか" #~ "らのメッセージはすべて無視されます。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一度確認すると、今後このキーを使って %s から送信されるメッセージは無視さ" #~ "れ、このキーを使ったご自身のメッセージも読み取れなくなります。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "一度確認すると、このキーを使って %s からのメッセージを送受信可能になりま" #~ "す。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "この連絡先の相手がアカウントに追加する新しい暗号化キーを自動的に許可しま" #~ "す。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "アカウントに追加した新しい暗号化キーを自動的に許可します。" dino-0.4.3/plugins/omemo/po/lb.po0000644000000000000000000002713314452563620015332 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-12 20:01+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Geräter managen" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Schlëssel verwalten" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergläich de Fangerofdrock, Zeeche fir Zeechen, mat dem den um Gerät vum " "Kontakt ugewise gëtt." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fangerofdréck stemmen net iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fangerofdréck stemmen iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Ofbriechen" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirméieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Schlëssel verifizéieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Zukünfteg Noriichten, déi vum Apparat vum %s, den dëse Schlëssel benotzt, " "geschéckt ginn, wäerten deementspriechend am Chatfenster ervirgehuewen ginn." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fangerofdréck stëmmen net iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Wann ech gelift iwwerpréif, ob's du mat dem " "korrekte Fangerofdrock vergläichs. Wann Fangerofdréck net iwwerteneestëmmen, " "dann ass dem %s säi Konto villäicht kompromettéiert an du solls dëse " "Schlëssel verweigeren." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Iwwerpréif Schlëssel Fangerofdrock" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Vergläich de Fangerofdrock vun dësem Schlëssel mam Fangerofdrock den um " "Gerät vun dësem Kontakt steet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Schlëssel ofleenen" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockéiert verschlësselt Kommunikatioun mam Apparat vum Kontakt deen dëse " "Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Schlëssel acceptéieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Erlaabt verschlësselt Kommunikatioun mam Apparat vum Kontakt, deen dëse " "Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Den aktuelle Schlëssel ass %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akzeptéiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dat bedeit, dass et vum %s ka benotzt gi fir Messagen ze empfänken an ze " "verschécken." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verifizéiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "De Schlëssel ass zousätzlech iwwerpréift gi mam Schlëssel um Gerät vum " "Kontakt iwwerteneen ze stëmmen." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ofgeleent" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Dëst bedeit datt et net vum %s ka benotzt ginn fir är Messagen ze " "entschlësselen, an Dir gesitt keng Messagen déi mat dësem verschlësselt sinn." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Dir gesitt net verschlësselt Message vum Apparat vum %s deen dëse Schlëssel " "benotzt. Ëmgekéiert kann dëse Apparat net méi Är Messagen entschlësselen." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Dir kënnt verschlësselte Messagen austausche mat dem Apparat vum %s deen " "dësen Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zeréck" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Verwalten" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Dëse Kontakt huet nei Geräter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO Vertrauens Entscheedung noutwenneg" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hues du een neit Gerät fir %s dobäi gemaach?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Eegene Fangerofdrock" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Gett bei der éischter Connectioun generéiert" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Verschlësselung" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO Gerät" msgstr[1] "%d OMEMO Geräter" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO Schlësselenverwaltung" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automatesch nei Schlësselen akzeptéieren" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Nei Verschlësslungs Schlëssele vun dem Kontakt ginn automatesch akzeptéiert." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Eegene Schlëssel" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nei Schlësselen" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Zougehéiereg Schlësselen" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inaktiv Schlësselen" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nei Verschlësslungs Schlëssele vun ären aneren Apparater ginn automatesch " "akzeptéiert." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Akzeptéiert" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Ofgeleent" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verifizéiert" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Onbenotzt" #~ msgid "Not matching" #~ msgstr "Stemmt net iwwerteneen" #~ msgid "Matching" #~ msgstr "Stemmt iwwerteneen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Wann konforméiert, all zukünfteg Messagen déi vum %s mat dësem Schlëssel " #~ "geschéckt ginn, ginn deementspriechend am Chatfenster markéiert." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dëse Schlëssel net méi akzeptéiere wärend der Kommunikatioun mat sengem " #~ "verbonnene Kontakt." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Dëse Schlëssel wärend de Conversatioun mat sengem Kontakt benotzen" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dat bedeit, dass dëse Schlëssel net ka vum %s benotz gi fir Messagen ze " #~ "empfänken an all Message déi geschéckt gi ginn ignoréiert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Wann confirméiert, all zukünfteg Message déi vum %s mat dësem Schlëssel " #~ "geschéckt ginn, ginn ignoréiert a keng vun dengem Messagë wäerte liesbar " #~ "sinn." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Wann konforméiert ass de Schlëssel vum %s benotzbar fir Messagen ze " #~ "empfänken an ze schécken." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Wann dëse Kontakt néi Schlësselen zu sengem Konto bäifüügt, déi " #~ "automatesch akzeptéieren." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Wann s du nei Schlëssele bei däin Account bäifüügs, déi automatesch " #~ "akzeptéieren." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dëse Schlëssel wärend der Kommunikatioun mat sengem verbonne Kontakt " #~ "akzeptéieren." #~ msgid "Reject Key" #~ msgstr "Schlëssel verweigeren" #~ msgid "Accept Key" #~ msgstr "Schlëssel acceptéieren" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Onbekannt Gerät (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Aner Geräter" #~ msgid "- None -" #~ msgstr "- Keng -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Datebank Feeler" dino-0.4.3/plugins/omemo/po/lt.po0000644000000000000000000002602514452563620015353 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-21 18:29+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s naudoja nepatikimą įrenginį. Jūs nematysite žinučių iš įrenginių, kuriais " "nepasitikite." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Tvarkyti įrenginius" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nepasitiki šiuo įrenginiu. Tai reiškia, kad galite nematyti žinučių." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Tvarkyti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Palyginkite kontrolinį kodą, simbolis po simbolio, su tuo, kuris yra rodomas " "jūsų adresato įrenginyje." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Kodas skiriasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Kodas atitinka" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Atsisakyti" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Patvirtinti" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Patikrinkite raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Nuo šiol žinutės nuo %s išsiųstos iš įrenginio naudojančio šį kontrolinį " "kodą bus atitinkamai paryškintos." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Kontroliniai kodai nesutampa" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Įsitikinkite, kad lyginate teisingą kontrolinį kodą. Jei kontroliniai kodai " "nesutampa, gali būti, kad %s paskyra yra sukompromituota ir turėtumėte " "apsvarstyti galimybę atmesti šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Patikrinti rakto kontrolinį kodą" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Palyginti šio rakto kontrolinį kodą su adresato įrenginyje rodomu " "kontroliniu kodu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Atmesti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokuoti šifruotą bendravimą su kontakto įrenginiu, kuris naudoja šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Priimti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Leisti šifruotą bendravimą su kontakto įrenginiu, kuris naudoja šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Šiuo metu šis raktas yra %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "priimtas" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Tai reiškia, kad %s gali jį naudoti šifruotų žinučių siuntimui ir gavimui." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "patikrintas" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Be to, jis buvo patikrintas ir sutampa su adresato įrenginyje rodomu raktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "atmestas" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Tai reiškia, kad %s negali jo naudoti jūsų žinučių iššifravimui ir jūs " "nematysite žinučiu užšifruotų šiuo raktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Jūs nematysite šifruotų žinučių išsiustų iš %s įrenginio, kuris naudoją šį " "raktą. Tuo pačiu tas įrenginys nebegalės iššifruoti jūsų žinučių." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Jūs galite keistis šifruotomis žinutėmis su %s įrenginiu, kuris naudoja šį " "raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atgal" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Tvarkyti" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Šis adresatas turi naujų įrenginių" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO reikia pasitikėjimo sprendimo" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ar savo paskyrai %s pridėjote naują įrenginį?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Nuosavas kontrolinis kodas" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Bus sugeneruotas pirmojo prisijungimo metu" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Šifravimas" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO įrenginys" msgstr[1] "%d OMEMO įrenginiai" msgstr[2] "%d OMEMO įrenginių" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO raktų tvarkymas" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automatiškai priimti naujus raktus" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Nauji šifravimo raktai iš šio kontakto bus priimti automatiškai." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Nuosavas raktas" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nauji raktai" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Susieti raktai" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Neaktyvūs raktai" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nauji šifravimo raktai iš jūsų kitų įrenginių bus priimti automatiškai." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Priimtas" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Atmestas" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Patikrintas" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Nenaudojamas" #~ msgid "Your contact" #~ msgstr "Jūsų adresatas" #~ msgid "Not matching" #~ msgstr "Nesutampa" #~ msgid "Matching" #~ msgstr "Sutampa" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Patikrinus, bet kokios būsimos žinutės, kurias %s išsiųs naudodamas šį " #~ "raktą, bus atitinkamai paryškintos pokalbių kambaryje." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Nustoti priiminėti šį raktą bendravimo su susietu adresatu metu." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Pradėti priiminėti šį raktą bendravimo su susietu adresatu metu" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Tai reiškia, kad %s negali jo naudoti, kad gautų žinutes, o bet kurių " #~ "adresato siunčiamų žinučių bus nepaisoma." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Patvirtinus, bet kurių būsimų žinučių, kurias %s išsiųs naudodamas šį " #~ "raktą, bus nepaisoma ir jokių jūsų žinučių nebus įmanoma perskaityti " #~ "naudojant šį raktą." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Patvirtinus, šis raktas taps tinkamu naudoti %s, kad gautų ir siųstų " #~ "žinutes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Kai šis adresatas prie savo paskyros pridės naujus šifravimo raktus, " #~ "automatiškai juos priimti." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Kai pridedate naujus šifravimo raktus prie savo paskyros, automatiškai " #~ "juos priimti." dino-0.4.3/plugins/omemo/po/nb.po0000644000000000000000000002657014452563620015340 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s har brukt en ubetrodd enhet. Du vil ikke se meldinger fra enheter du ikke " "har tiltro til." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Håndter enheter" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s har ikke tiltro til denne enheten. Det betyr at du kan gå glipp av " "meldinger." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Håndter nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Sammenlign fingeravtrykket, tegn for tegn, med den vist på din kontakts " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingeravtrykkene stemmer ikke overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingeravtrykkene stemmer overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Avbryt" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bekreft" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Bekreft nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Fremtidige meldinger sendt av %s fra enheten som benytter denne nøkkelen vil " "framheves i så måte i sludrevinduet." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingeravtrykkene stemmer ikke overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Sjekk at du sammenligner riktig fingeravtrykk. Hvis de ikke stemmer overens, " "kan det hende %s sin konto har falt i gale hender, og du bør overveie å " "avvise denne nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Sjekk nøkkelfingeravtrykk" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Sammenlign dette fingeravtrykket med fingeravtrykket vist på kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Avslå nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokker kryptert kommunikasjon med kontaktens enhet som benytter denne " "nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Godta nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Tillat kryptert kommunikasjon med kontaktens enhet som benytter denne " "nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Denne nøkkelen er i øyeblikket %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "godtatt" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dette betyr at den kan brukes av %s til å motta og sende krypterte meldinger." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "bekreftet" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "I tillegg har den blitt bekreftet å samsvare med nøkkelen på kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "avslått" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Dette betyr den ikke kan brukes av %s for å fortolke meldingene dine, og du " "vil ikke kunne se meldinger kryptert med den." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du vil ikke se krypterte meldinger fra %s sin enhet som bruker denne " "nøkkelen. Denne enheten vil heller ikke kunne fortolke dine meldinger lenger." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du vil kunne utveksle krypterte meldinger med %s sin enhet som benytter " "denne nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tilbake" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Behandle" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Denne kontakten har nye enheter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO tillitsbeslutning kreves" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "La du til en ny enhet for kontoen %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Eget fingeravtrykk" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Vil bli generert ved første tilkobling" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Kryptering" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO-enhet" msgstr[1] "%d OMEMO-enheter" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO-nøkkelhåndtering" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Godta nye nøkler automatisk" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Nye krypteringsnøkler fra denne kontakten vil godtas automatisk." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Egen nøkkel" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nye nøkler" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Tilknyttede nøkler" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inaktive nøkler" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Nye krypteringsnøkler fra dine andre enheter vil godtas automatisk." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Godtatt" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Avslått" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Bekreftet" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Ubrukt" #~ msgid "Your contact" #~ msgstr "Din kontakt" #~ msgid "Not matching" #~ msgstr "Samsvarer ikke" #~ msgid "Matching" #~ msgstr "Samsvarer" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Når først bekreftet, vil fremtidige meldinger sendt av %s ved bruk av " #~ "denne nøkkelen bli fremhevet tilsvarende i sludrevinduet." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Slutt å godta denne nøkkelen under kommunikasjon med dens tilknyttede " #~ "kontakt." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Aksepter heretter denne nøkkelen i kommunikasjon med dens tilhørende " #~ "kontakt" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dette betyr at den ikke kan brukes av %s til å motta meldinger, og enhver " #~ "melding sendt på vegne av den vil bli ignorert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Etter bekreftelsen vil fremtidige meldinger sendt av %s der denne " #~ "nøkkelen brukes bli ignorert, og ingen av dine meldinger vil kunne leses " #~ "med den." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Når først bekreftet vil denne nøkkelen kunne brukes av %s til å motta og " #~ "sende meldinger." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Når denne kontakten legger nye krypteringsnøkler til kontoen sin, godta " #~ "dem automatisk." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Godta dem automatisk når du legger nye krypteringsnøkler til kontoen din." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Begynn å godta denne nøkkelen under kommunikasjon med dens tilknyttede " #~ "kontakt." #~ msgid "Reject Key" #~ msgstr "Avslå nøkkel" #~ msgid "Accept Key" #~ msgstr "Godta nøkkel" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Ukjent enhet (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andre enheter" #~ msgid "- None -" #~ msgstr "- Ingen -" #~ msgid "OMEMO" #~ msgstr "OMEMO" dino-0.4.3/plugins/omemo/po/nl.po0000644000000000000000000002665114452563620015352 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s is een onvertrouwd apparaat. Berichten van onvertrouwde apparaten worden " "niet getoond." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Apparaten beheren" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s vertrouwt dit apparaat niet. Je mist hierdoor mogelijk berichten." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Sleutel beheren" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergelijk de vingerafdruk, letter voor letter, met die op het apparaat van " "je contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Vingerafdrukken wijken af" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Vingerafdrukken komen overeen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annuleren" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bevestigen" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Sleutel verifiëren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Toekomstige berichten van %s van het apparaat dat deze sleutel gebruikt, " "worden gemarkeerd in het gespreksvenster." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Vingerafdrukken komen niet overeen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Zorg dat je de juiste vingerafdrukken vergelijkt. Als de vingerafdrukken " "niet overeenkomen, kan het zijn dat het account van %s gehackt is. Overweeg " "in dat geval deze sleutel te weigeren." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Vingerafdruk van sleutel verifiëren" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Vergelijk de vingerafdruk van deze sleutel met de vingerafdruk die wordt " "getoond op het apparaat van je contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Sleutel weigeren" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokkeer versleutelde communicatie met het apparaat van deze contactpersoon " "dat deze sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Sleutel accepteren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Sta versleutelde communicatie toe met het apparaat van deze contactpersoon " "dat deze sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Deze sleutel is momenteel %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "geaccepteerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dit betekent dat %s deze kan gebruiken om berichten te ontvangen en " "versturen." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "geverifieerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Er is vastgesteld dat deze sleutel overeenkomt met de sleutel op het " "apparaat van de contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "geweigerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "De sleutel kan niet door %s worden gebruikt om berichten te ont- en " "versleutelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Je ziet geen versleutelde berichten van het apparaat van %s dat deze sleutel " "gebruikt. Daarnaast kan het apparaat jouw berichten niet meer ontsleutelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Je kunt versleutelde berichten uitwisselen met het apparaat van %s dat deze " "sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Terug" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Beheren" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Deze contactpersoon heeft nieuwe apparaten" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-vertrouwenskeuze vereist" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Heb je een nieuw apparaat toegevoegd aan %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Mijn vingerafdruk" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Wordt bij de eerste verbinding gegenereerd" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Versleuteling" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO-apparaat" msgstr[1] "%d OMEMO-apparaten" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO-sleutelbeheer" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Nieuwe sleutels automatisch accepteren" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Nieuwe sleutels van deze contactpersoon worden automatisch geaccepteerd." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Mijn sleutel" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nieuwe sleutels" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Geassocieerde sleutels" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inactieve sleutels" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nieuwe sleutels van je andere apparaten worden automatisch geaccepteerd." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Geaccepteerd" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Geweigerd" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Geverifieerd" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Ongebruikt" #~ msgid "Your contact" #~ msgstr "Je contactpersoon" #~ msgid "Not matching" #~ msgstr "Komen niet overeen" #~ msgid "Matching" #~ msgstr "Komen overeen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Zodra bevestigd, worden nieuwe berichten verstuurd door %s met deze " #~ "sleutel uitgelicht in het chatvenster." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepteer niet langer deze sleutel tijdens communicatie met het " #~ "bijbehorende contact." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Accepteer deze sleutel tijdens communicatie met het bijbehorende contact" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dit betekent dat het niet gebruikt kan worden door %s om berichten te " #~ "ontvangen. Berichten die hiermee verstuurd worden worden genegeerd." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Zodra bevestigd, worden toekomstige berichten die verstuurd worden door " #~ "%s met deze sleutel genegeerd en zullen geen van je berichten leesbaar " #~ "zijn met deze sleutel." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Zodra bevestigd kan deze sleutel gebruikt worden door %s om berichten te " #~ "ontvangen en te versturen." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Indien dit contact nieuwe encryptiesleutels toevoegt aan hun account, " #~ "automatisch accepteren." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Nieuwe encryptiesleutels die aan het account worden toegevoegd " #~ "automatisch accepteren." #, fuzzy #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepteer deze sleutel tijdens communicatie met het bijbehorende contact" #~ msgid "Reject Key" #~ msgstr "Sleutel Afwijzen" #~ msgid "Accept Key" #~ msgstr "Sleutel Aanvaarden" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Onbekend apparaat (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andere apparaten" #~ msgid "- None -" #~ msgstr "- Geen -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Databasefout" dino-0.4.3/plugins/omemo/po/oc.po0000644000000000000000000001714114452563620015334 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-05-26 13:19+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.13-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilizat un periferic desconegut. Veiretz pas los messatges venents de " "client que vos fisatz pas." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gerir los periferics" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s se fisa pas d’aqueste client. Significa que mancaretz benlèu de messatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestion de las claus" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparatz l’emprenta, caractèr per caractèr, amb aquela qu’es afichada al " "client de vòstre contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "L’emprenta es diferenta" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Las emprentas correspondon" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anullar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Las emprentas correspondon pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar l’emprenta d’aquesta clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparatz aquesta clau amb l’emprenta afichada al periferic de vòstre " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Regetar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocar las comunicacion chifradas amb lo client del contacte qu’utiliza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Acceptar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "La clau es actualament %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "regetada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tornar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerir" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Chiframent" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d periferic OMEMO" msgstr[1] "%d periferics OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gestion de las claus OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Acceptada" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Regetada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "" #~ msgid "Your contact" #~ msgstr "Vòstre contacte" dino-0.4.3/plugins/omemo/po/pl.po0000644000000000000000000002656214452563620015355 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-01-20 15:56+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s używa niezaufanego urządzenia. Nie będziesz widzieć wiadomości wysłanych " "z urządzeń, którym nie ufasz." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Zarządzaj urządzeniami" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nie ufa temu urządzeniu. Oznacza to, że wiadomości mogą do ciebie nie " "docierać." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Zarządzaj kluczem" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Porównaj odcisk klucza, znak po znaku, z tym pokazanym na urządzeniu twojego " "kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Odcisk klucza różni się" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Odcisk klucza pasuje" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anuluj" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Potwierdź" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Zweryfikuj klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Przyszłe wiadomości wysyłane przez %s z urządzenia używającego tego klucza " "zostaną odpowiednio podświetlone w oknie czatu." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Odcisk klucza nie zgadza się" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Proszę zweryfikować, że porównujesz prawidłowy odcisk klucza. Jeśli odciski " "nie zgadzają się, konto %s mogło być zaatakowane i należy rozważyć " "odrzucenie tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Zweryfikuj odcisk klucza" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Porównaj odcisk tego klucza z odciskiem wyświetlonym na urządzeniu kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Odrzuć klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokuj szyfrowaną komunikację z urządzeniem kontaktu, które korzysta z tego " "klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Zaakceptuj klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Zezwól na szyfrowaną komunikację z urządzeniem kontaktu, które korzysta z " "tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ten klucz jest obecnie %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "zaakceptowany" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "To znaczy, że może być używany przez %s do odbierania i wysyłania " "szyfrowanych wiadomości." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "zweryfikowany" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Dodatkowo zostało sprawdzone, że zgadza się z kluczem na urządzeniu kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "odrzucony" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Oznacza to, że %s nie może go użyć do odszyfrowania wiadomości i nie " "zobaczysz wiadomości zaszyfrowanych przy jego pomocy." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nie zobaczysz zaszyfrowanych wiadomości z urządzenia %s, które używa tego " "klucza. I odwrotnie, to urządzenie nie będzie już w stanie odszyfrować " "Twoich wiadomości." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Będziesz mógł wymieniać zaszyfrowane wiadomości z urządzeniem %s, które " "używa tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Wstecz" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Zarządzaj" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ten kontakt ma nowe urządzenia" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decyzja o zaufaniu OMEMO jest potrzebna" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Czy dodałeś nowe urządzenie dla konta %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Własny odcisk klucza" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Zostanie wygenerowany przy pierwszym połączeniu" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Szyfrowanie" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d urządzenie OMEMO" msgstr[1] "%d urządzenia OMEMO" msgstr[2] "%d urządzeń OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Zarządzanie kluczami OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Automatycznie akceptuj nowe klucze" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Nowe klucze szyfrowania od tego kontaktu będą akceptowane automatycznie." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Własny klucz" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Nowe klucze" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Pozostałe klucze" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Nieaktywne klucze" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nowe klucze szyfrowania z innych urządzeń będą akceptowane automatycznie." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Zaakceptowany" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Odrzucony" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Zweryfikowany" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Nieużywany" #~ msgid "Not matching" #~ msgstr "Odciski nie zgadzają się" #~ msgid "Matching" #~ msgstr "Odciski zgadzają się" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Przyszłe wiadomości wysłane przez %s i szyfrowane tym kluczem będą " #~ "odpowiednio zaznaczone w oknie czatu." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Zablokuj szyfrowaną komunikację ze sprzętem kontaktu, który używa ten " #~ "klucz." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Włącz szyfrowaną komunikację ze sprzętem kontaktu, który używa ten klucz." #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "To znaczy, że nie może być używany przez %s do rozszyfrowania twoich " #~ "wiadomości, a wiadomości szyfrowane nim nie będą u ciebie wyświetlane." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Wiadomości wysłane przez %s ze sprzętu używającego ten klucz nie będą " #~ "wyświetlane. Podobnie sprzęt tej osoby nie będzie mógł rozszyfrować " #~ "twoich wiadomości." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "To umożliwi wymianę szyfrowanych wiadomości ze sprzętem należącym do %s, " #~ "który używaja ten klucz." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Gdy ten kontakt doda nowe klucze szyfrowania do swojego konta, zaakceptuj " #~ "je automatycznie." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Gdy dodam nowe klucze szyfrowania do mojego konta, zaakceptuj je " #~ "automatycznie." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Nieznane urządzenie (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Pozostałe urządzenia" #~ msgid "- None -" #~ msgstr "- Wybierz -" dino-0.4.3/plugins/omemo/po/pt.po0000644000000000000000000002202014452563620015346 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-05-10 09:33+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.7-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s esteve a usar um aparelho não confiável. Não verá mensagens de aparelhos " "nos quais não confia." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gerir aparelhos" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s não confia neste aparelho. Isso significa que pode estar a perder " "mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerir Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compare a impressão digital, caractere a caractere, com aquela mostrada no " "aparelho de seu contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impressões digitais não correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impressões digitais correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mensagens futuras enviadas por %s do aparelho que usa essa chave serão " "destacadas no janela de bate papo." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Impressões digitais não correspondem" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor verifique se está a comparar as impressões digitais corretas. Caso " "elas não correspondam, a conta do %s pode estar comprometida e deve " "considerar rejeitar essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar impressão digital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compare a impressão digital dessa chave com a impressão digital exibida no " "aparelho do contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rejeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloqueie comunicação criptografada com o aparelho do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Aceitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permita comunicação criptografada com o aparelho do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Essa chave está atualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceita" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Isso significa que ela pode ser usada por %s para enviar e receber mensagens " "criptografadas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Além disso, verificou-se que ela corresponde com a chave no aparelho do " "contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Isso significa que ela não pode ser usada por %s para decifrar suas " "mensagens, e não irá visualizar mensagens criptografadas com ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Não irá visualizar mensagens criptografas pelo aparelho de %s que usa essa " "chave. Além disso, esse aparelho não será mais capaz de decifrar suas " "mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Poderá trocar mensagem criptografadas com o aparelho de %s que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Voltar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerir" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Esse contato possui novos aparelhos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisão de confiança OMEMO necessária" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Adicionou um novo aparelho para a conta %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Impressão digital própria" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Será gerada na primeira conexão" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Criptografia" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d aparelho OMEMO" msgstr[1] "%d aparelhos OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gestão de chave OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Aceitar novas chaves automaticamente" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Novas chaves de criptografia desse contato serão aceitas automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Chave própria" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Chaves novas" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Chaves associadas" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Chaves inativas" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novas chaves de criptografia de seus outros aparelhos serão aceitas " "automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Aceita" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rejeitada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Não usada" #~ msgid "Your contact" #~ msgstr "O seu contacto" dino-0.4.3/plugins/omemo/po/pt_BR.po0000644000000000000000000002604614452563620015745 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-22 03:26+0000\n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.3.1\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s tem usado um dispositivo não confiável. Você não verá mensagens de " "dispositivos que você não confia." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gerenciar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s não confia nesse dispositivo. Isso significa que você pode estar perdendo " "algumas mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerenciar Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compare a impressão digital, caractere a caractere, com aquela mostrada no " "dispositivo de seu contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impressões digitais não correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impressões digitais correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mensagens futuras enviadas por %s do dispositivo que usa essa chave serão " "destacadas no janela de bate papo." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Impressões digitais não correspondem" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor verifique se você está comparando as impressões digitais corretas. " "Caso elas não correspondam, a conta do %s pode estar comprometida e você " "deve considerar rejeitar essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar impressão digital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compare a impressão digital dessa chave com a impressão digital exibida no " "dispositivo do contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Rejeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloqueie comunicação criptografada com o dispositivo do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Aceitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permita comunicação criptografada com o dispositivo do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Essa chave está atualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceita" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Isso significa que ela pode ser usada por %s para enviar e receber mensagens " "criptografadas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Além disso, verificou-se que ela corresponde com a chave no dispositivo do " "contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Isso significa que ela não pode ser usada por %s para decifrar suas " "mensagens, e você não irá visualizar mensagens criptografadas com ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Você não irá visualizar mensagens criptografas pelo dispositivo de %s que " "usa essa chave. Além disso, esse dispositivo não será mais capaz de decifrar " "suas mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Você poderá trocar mensagem criptografadas com o dispositivo de %s que usa " "essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Voltar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerenciar" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Esse contato possui novos dispositivos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisão de confiança OMEMO necessária" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Você adicionou um novo dispositivo para a conta %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Impressão digital própria" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Será gerada na primeira conexão" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Criptografia" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispositivo OMEMO" msgstr[1] "%d dispositivos OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Gerenciamento de Chave OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Aceitar novas chaves automaticamente" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Novas chaves de criptografia desse contato serão aceitas automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Chave própria" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Chaves novas" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Chaves associadas" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Chaves inativas" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novas chaves de criptografia de seus outros dispositivos serão aceitas " "automaticamente." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Aceita" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Rejeitada" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Não usada" #~ msgid "Your contact" #~ msgstr "Seu contato" #~ msgid "Not matching" #~ msgstr "Não correspondem" #~ msgid "Matching" #~ msgstr "Correspondem" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Uma vez confirmada, toda mensagem enviada por %s no futuro será destacada " #~ "na janela da conversa." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Pare de aceitar essa chave ao se comunicar com o contato associado." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Aceite essa chave ao se comunicar com o contato associado" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Isso significa que ela não pode ser usada por %s para receber mensagens e " #~ "qualquer mensagem enviada com ela será ignorada." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Uma vez confirmada, toda mensagem enviada por %s usando essa chave será " #~ "ignorada e nenhuma de suas mensagens com ela serão legíveis." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Uma vez confirmada, essa chave poderá ser usada por %s para receber e " #~ "enviar mensagens." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quando esse contato adicionar novas chaves de criptografia à conta, " #~ "aceite-as automaticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quando você adicionar novas chaves de criptografia para sua conta, aceite-" #~ "as automaticamente." dino-0.4.3/plugins/omemo/po/ro.po0000644000000000000000000002745314452563620015362 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-13 09:26+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilizat un dispozitiv care nu este de încredere. Nu veți vedea mesaje " "de pe dispozitive în care nu aveți încredere." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Gestionare dispozitive" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nu are încredere în acest dispozitiv. Asta înseamnă că s-ar putea să-ți " "lipsească mesaje." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestionare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparați amprenta, caracter cu caracter, cu amprenta afișată pe " "dispozitivul contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Amprentele diferă" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Amprentele se potrivesc" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anulare" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmare" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesajele viitoare trimise de %s de pe dispozitivul care utilizează această " "cheie vor fi evidențiate în mod corespunzător în fereastra de chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Amprentele nu se potrivesc" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Vă rugäm să verificați dacă se compară amprentele corecte. Dacă nu se " "potrivesc amprentele s-ar putea ca %s să aibä un cont compromis și ar trebui " "să luați în considerare respingerea acestei chei." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificare amprentă cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparați amprenta cheii cu amprenta afișată pe dispozitivul contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Respingere cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocați comunicarea criptată cu dispozitivul persoanei de contact care " "utilizează această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Acceptare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permiteți comunicarea criptată cu dispozitivul persoanei de contact care " "utilizează această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Cheia este în prezent %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptată" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Aceasta înseamnă că poate fi utilizată de %s pentru a primi și a trimite " "mesaje criptate." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificată" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "În plus, a fost verificată că se potrivește cu cheia de pe dispozitivul " "contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "respinsă" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Aceasta înseamnă că nu poate fi utilizată de %s pentru a vă descifra " "mesajele și nu veți vedea mesajele criptate cu aceasta." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nu veți vedea mesaje criptate de la dispozitivul %s care utilizează această " "cheie. În schimb, dispozitivul respectiv nu va mai putea descifra mesajele " "dumneavoastră." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Veți putea schimba mesaje criptate cu dispozitivul %s care utilizează " "această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Înapoi" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestionare" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Acest contact are dispozitive noi" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Este necesara luarea unei decizii în privința OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ați adăugat un nou dispozitiv pentru contul %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Amprentă proprie" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Se va genera la prima conectare" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Criptare" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d dispozitiv OMEMO" msgstr[1] "%d dispozitive OMEMO" msgstr[2] "%d de dispozitive OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Administrare chei OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Acceptă automat chei noi" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Noile chei de criptare de la această persoană de contact vor fi acceptate " "automat." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Cheie proprie" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Chei noi" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Chei asociate" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Chei inactive" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Noile chei de criptare de pe celelalte dispozitive ale dumneavoastră vor fi " "acceptate automat." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Acceptată" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Respinsă" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verificată" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Neutilizatä" #~ msgid "Your contact" #~ msgstr "Persoana de contact" #~ msgid "Not matching" #~ msgstr "Nu se potrivesc" #~ msgid "Matching" #~ msgstr "Se potrivesc" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Odată confirmată, orice viitoare mesaje trimise de %s folosind această " #~ "cheie vor fi evidenţiate în consecință în fereastra de discuție." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Nu mai accepta această cheie în timpul comunicärii cu acest contact " #~ "asociat." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Se va accepta această cheie în timpul comunicării cu acest contact asociat" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Aceasta înseamnă că nu poate fi folosită de %s ca să primească mesaje, " #~ "iar orice mesaj trimis cu ea v-a fi ignorat." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Odată confirmată, orice viitoare mesaje trimise de %s folosind această " #~ "cheie vor fi ignorate și nici un mesaj trimis de dumneavoastră nu se va " #~ "putea citii folosid aceastä cheie." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Odată confirmată, aceastä cheie va fi utilizatä de %s pentru a primi și " #~ "trimite mesaje." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Când acest contact adäugă chei de criptare noi în contul lor, acestea vor " #~ "fi acceptate în mod automat." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Când adäugați chei de criptare noi pentru acest cont, acestea vor fi " #~ "acceptate în mod automat." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Acceptă această cheie în timpul comunicării cu acest contact asociat." #~ msgid "Reject Key" #~ msgstr "Respingere cheie" #~ msgid "Accept Key" #~ msgstr "Acceptare cheie" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispozitiv necunoscut (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Alte dispozitive" #~ msgid "- None -" #~ msgstr "- Nici unul -" dino-0.4.3/plugins/omemo/po/ru.po0000644000000000000000000003273514452563620015367 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s использовал(а) ненадёжное устройство. Вы не будете видеть сообщения от " "устройств, которым не доверяете." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Управление устройствами" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s не доверяет этому устройству. Это означает, что у вас могут отсутствовать " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Управление ключом" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Сравните отпечаток, символ за символом, с тем, который показан на устройстве " "вашего контакта." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Отпечатки отличаются" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Отпечатки схожи" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Отмена" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Подтвердить" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Подтвердить ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Будущие сообщения, отправленные %s с устройства, использующего этот ключ, " "будут соответствующим образом выделены в окне чата." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Отпечатки не совпадают" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Пожалуйста, проверьте совпадение отпечатков. Если они не совпадают, аккаунт " "\"%s\" может быть скомпрометирован, и вам стоит отклонить этот ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Подтвердить отпечаток" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "Сверить этот отпечаток с ключом, отображаемым у контакта." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Отклонить ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Заблокировать зашифрованную связь с устройством контакта, использующего этот " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Принять ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Разрешает зашифрованную связь с устройством контакта, использующего этот " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ключ был успешно %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "принят" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Это означает, что %s теперь может отправлять и принимать зашифрованные " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "проверен" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Также была проведена проверка совпадения ключа." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "отклонён" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Это означает что они не смогут использоваться %s для расшифровки ваших " "сообщений, и вы также не увидите сообщений зашифрованных ими." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Вы не увидите зашифрованных сообщений от устройства %s, которое использует " "этот ключ. И наоборот, это устройство больше не сможет расшифровывать ваши " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Вы сможете обмениваться зашифрованными сообщениями с устройством %s, которое " "использует этот ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Назад" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Настроить" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Этот контакт воспользовался новым устройством" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Требуется решение OMEMO о доверии" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Вы добавляли аккаунт %s на новое устройство?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Отпечаток этого устройства" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Будет сгенерирован при первом подключении" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Шифрование" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d устройство OMEMO" msgstr[1] "%d устройства OMEMO" msgstr[2] "%d устройств OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Управление ключами OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Автоматически принимать новые ключи" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Новые ключи шифрования от этого контакта будут приняты автоматически." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Собственный ключ" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Новые ключи" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Связанные ключи" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Неактивные ключи" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Новые ключи шифрования от других ваших устройств будут приняты автоматически." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Принят" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Отклонён" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Подтверждён" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Не используется" #~ msgid "Your contact" #~ msgstr "Ваш контакт" #~ msgid "Not matching" #~ msgstr "Не совпадает" #~ msgid "Matching" #~ msgstr "Совпадает" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "После подтверждения все последующие сообщения, отправленные с %s при " #~ "помощи этого ключа, будут соответственно подсвечены в окне чата." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Не принимать ключ этого контакта." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Принять ключ этого контакта" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Это значит, что %s не может отправлять сообщения — все они будут " #~ "игнорироваться." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "После подтверждения все будущие сообщения, отправленные %s с " #~ "использованием этого ключа, будут игнорироваться и ни одно из ваших " #~ "сообщений не сможет быть прочитано с использованием этого ключа." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "После подтверждения этот ключ будет использоваться %s для получения и " #~ "отправки сообщений." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Когда этот контакт добавляет новые ключи шифрования в свою учетную " #~ "запись, автоматически принимайте их." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Когда вы добавляете новые ключи шифрования в свою учетную запись, " #~ "автоматически принимайте их." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Неизвестное устройство (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Другие устройства" #~ msgid "- None -" #~ msgstr "- Нет -" dino-0.4.3/plugins/omemo/po/sq.po0000644000000000000000000002214414452563620015355 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-04 15:20+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s ka përdorur një pajisje jo të besuar. S’do të shihni mesazhe prej " "pajisjesh që nuk i besoni." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Administroni pajisje" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nuk e beson këtë pajisje. Kjo do të thotë, mund të jeni duke humbur " "mesazhe." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Administroni Kyç" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Krahasoni shenjën e gishtave, shenjë pas shenje, me atë të shfaqur në " "pajisjen e kontaktit tuaj." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Shenjat e gishtave janë të ndryshme" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Shenjat e gishtave përputhen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anuloje" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Ripohojeni" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifikoni kyç" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesazhe të ardhshme dërguar nga %s prej pajisjes që përdor këtë kyç do të " "theksohen te dritarja e fjalosjes." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Shenjat e gishtave nuk përputhen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ju lutemi, verifikoni se po krahasoi shenjat e sakta të gishtave. Nëse " "shenjat e gishtave nuk përputhen, llogaria e %s mund të komprometohet dhe " "duhet të shihni mundësinë e hedhjes poshtë të këtij kyçi." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifikoni shenja gishtash kyçi" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Krahasoni shenjën e gishtit të këtij kyçi me shenjën e gishtit të shfaqur në " "pajisjen e kontaktit." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Hidhe tej kyçin" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blloko komunikim të fshehtëzuar me pajisjen e kontaktit që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Pranoje kyçin" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Lejo komunikim të fshehtëzuar me pajisjen e kontaktit që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ky kyç aktualisht është %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "i pranuar" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Kjo do të thotë se mund të përdoret nga %s për të marrë dhe dërguar mesazhe " "të fshehtëzuara." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "i verifikuar" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Për më tepër, është verifikuar për t’u përputhur me kyçin në pajisjen e " "kontaktit." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "i hedhur poshtë" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Kjo do të thotë se s’mund të përdoret nga %s për të deshifruar mesazhet " "tuaja, dhe s’do të shihni dot mesazhe të fshehtëzuara me të." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "S’do të shihni dot mesazhe të fshehtëzuar prej pajisjes së %s që përdor këtë " "kyç. Më anë tjetër, ajo pajisje s’do të jetë në gjendje të deshifrojë më " "mesazhet tuaja." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Do të jeni në gjendje të shkëmbeni mesazhe të fshehtëzuar me pajisjen e %s " "që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Mbrapsht" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Administroni" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Ky kontakt ka pajisje të reja" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Lypset vendim besimi OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Shtuat një pajisje të re për llogarinë %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Shenjë e vet gishtash" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Do të prodhohet gjatë lidhjes së parë" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Fshehtëzim" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d pajisje OMEMO" msgstr[1] "%d pajisje OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "Administrim Kyçesh OMEMO" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Prano automatikisht kyçe të rinj" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Kyçe të rinj fshehtëzimi nga ky kontakt do të pranohen automatikisht." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Kyç i vet" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Kyçe të rinj" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Kyçe të përshoqëruar" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Kyçe jo aktivë" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Kyçe të rinj fshehtëzimi prej pajisjesh tuaja të tjera do të pranohen " "automatikisht." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Të pranuar" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Të hedhur tej" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Të verifikuar" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Të papërdorur" dino-0.4.3/plugins/omemo/po/sv.po0000644000000000000000000002551014452563620015362 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-05 16:50+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s har använt en ej betrodd enhet. Du kommer inte se meddelanden från " "enheter du inte litar på." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Hantera enheter" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s litar inte på den här enheten. Du kanske inte ser alla meddelanden." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Hantera nyckel" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Jämför fingeravtrycket, tecken för tecken, med det som visas i din kontakts " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingeravtrycken matcher ej" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingeravtrycken matchar" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Avbryt" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bekräfta" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifiera nyckel" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Framtida meddelanden skickade av %s från enheten som använder denna nyckel " "kommer tydligt att markeras i chattfönstret." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingeravtrycken överensstämmer inte" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Kontrollera att du jämför rätt fingeravtryck. Om fingeravtrycken inte " "stämmer så kan %s konto vara underminerat och du bör överväga att avvisa " "denna nyckel." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifiera nyckelns fingeravtryck" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Jämför nyckelns fingeravtryck med det fingeravtryck som visas i kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Avvisa nyckeln" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockera krypterad kommunikation med kontaktens enhet som använder den här " "nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Acceptera nyckeln" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Tillåt krypterad kommunikation med kontaktens enhet som använder den här " "nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Denna nyckel är för närvarande %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "accepterad" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Det betyder att den kan användas av %s för att skicka och ta emot krypterade " "meddelanden." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verifierad" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Dessutom har den verifierats så att den överensstämmer med nyckeln på " "kontaktens enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "avvisad" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Det innebär att den inte kan användas av %s för att dechiffrera dina " "meddelanden och du ser inte meddelanden som krypterats med den." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du ser inte krypterade meddelanden från enheten %s som använder den här " "nyckeln. Omvänt kan den enheten inte dechiffrera dina meddelanden längre." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du kommer att kunna utbyta krypterade meddelanden med enheten %s som " "använder den här nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tillbaka" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Hantera" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Denna kontakt har nya enheter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-tillitsbeslut krävs" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Har du lagt till en ny enhet för kontot %s?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Eget fingeravtryck" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "Genereras vid första anslutningstillfället" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Kryptering" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO-enhet" msgstr[1] "%d OMEMO-enheter" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO-nyckelhantering" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Acceptera automatiskt nya nycklar" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "Nya krypteringsnycklar från den här kontakten accepteras automatiskt." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Egen nyckel" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Ny nyckel" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "Kopplade nycklar" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Inaktiva nycklar" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Nya krypteringsnycklar från dina andra enheter accepteras automatiskt." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Accepterad" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Avvisad" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Verifierad" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Oanvänd" #~ msgid "Your contact" #~ msgstr "Din kontakt" #~ msgid "Not matching" #~ msgstr "Stämmer ej" #~ msgid "Matching" #~ msgstr "Stämmer" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Om du går vidare så kommer framtida meddelanden från %s med denna nyckel " #~ "att markeras i enlighet med nyckelns nya status." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Sluta acceptera denna nyckel vid kommunikation med den tillhörande " #~ "kontakten." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Börja acceptera nyckeln för kommunikation med den tillhörande kontakten" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Det betyder att den inte kan användas av %s för att ta emot meddelanden, " #~ "och att alla skickade meddelanden kommer att ignoreras." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Om du går vidare så kommer framtida meddelanden från %s som använder " #~ "denna nyckel att ignoreras, och meddelanden från dig blir oläsbara med " #~ "denna nyckel." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Om du går vidare så kommer nyckeln kunna användas av %s för att skicka " #~ "och ta emot meddelanden." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "Acceptera automatiskt nya nycklar som läggs till av denna kontakt." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "Acceptera automatiskt nya nycklar som du läggs till ditt konto." dino-0.4.3/plugins/omemo/po/tr.po0000644000000000000000000002565514452563620015371 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-07-30 08:33+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.7.2-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s güvenilmeyen bir cihaz kullanıyor. Güvenmediğiniz cihazlardan gelen " "mesajları görmezsiniz." #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Cihazları yönet" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s bu cihaza güvenmiyor. Bu, eksik mesajlarınız olabileceği anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Anahtarı Yönet" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Parmak izinin her bir karakterini, kişinizin cihazında gösterilen karakterle " "karşılaştırın." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Parmak izleri farklı" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Parmak izleri eşleşiyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "İptal" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Onayla" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Anahtarı doğrula" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Bu anahtarı kullanan cihazdan %s tarafından gönderilen gelecekteki mesajlar, " "sohbet penceresinde vurgulanacaktır." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Parmak izleri uyuşmuyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Lütfen doğru parmak izini karşılaştırdığınızdan emin olun. Parmak izleri " "eşleşmezse, %s hesabının güvenliği ihlal edilmiş olabilir ve bu anahtarı " "reddetmenizi öneririz." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Anahtar parmak izini doğrulayın" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Bu anahtarın parmak izini, kişinin cihazında görüntülenen parmak izi ile " "karşılaştırın." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Anahtarı reddet" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "Bu anahtarı kullanan kişinin cihazıyla şifreli iletişimi engelle." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "Kabul" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "Bu anahtarı kullanan kişinin cihazıyla şifreli iletişime izin ver." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Bu anahtar şu anda %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "kabul ediliyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Bu, şifreli mesajlar almak ve göndermek için %s tarafından kullanılabileceği " "anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "onaylı" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Ayrıca, kişinin cihazındaki anahtarla eşleştiği doğrulandı." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "reddedildi" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Bu, mesajlarınızın şifresini çözmek için %s tarafından kullanılamayacağı ve " "onunla şifrelenmiş mesajları göremeyeceğiniz anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Bu anahtarı kullanan %s cihazından gelen şifreli mesajları görmeyeceksiniz. " "Diğer taraftan, o cihaz artık mesajlarınızın şifresini çözemeyecek." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Bu anahtarı kullanan %s cihazıyla şifreli mesaj alışverişi yapabileceksiniz." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Geri" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Yönet" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "Bu kişinin yeni cihazları var" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO güven kararı gerekli" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "%s hesabı için yeni bir cihaz eklediniz mi?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "Kendi parmak iziniz" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "İlk bağlantıda oluşturulacak" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "Şifreleme" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d OMEMO aygıtı" msgstr[1] "%d OMEMO aygıtı" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO Anahtar Yönetimi" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "Yeni anahtarları otomatik olarak kabul et" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" "Bu kişiden gelen yeni şifreleme anahtarları otomatik olarak kabul edilecek." #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "Kendi anahtarınız" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "Yeni anahtarlar" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "İlişkili anahtarlar" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "Etkin olmayan anahtarlar" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Diğer cihazlarınızdan gelen yeni şifreleme anahtarları otomatik olarak kabul " "edilecek." #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "Kabul edildi" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "Reddedildi" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "Onaylandı" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "Kullanılmamış" #~ msgid "Your contact" #~ msgstr "Kişiniz" #~ msgid "Not matching" #~ msgstr "Eşleşmiyor" #~ msgid "Matching" #~ msgstr "Eşleşti" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Onaylandıktan sonra, bu anahtarı kullanarak %s tarafından gönderilen " #~ "gelecek iletiler sohbet penceresinde buna göre vurgulanır." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "İlişkili kişiyle iletişim kurarken bu anahtarı kabul etmeyi durdurun." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "İlişkili kişiyle iletişim için bu anahtarı kabul edin" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Bu, %s tarafından ileti almak için kullanılamayacağı ve gönderdiği " #~ "iletilerin yok sayılacağı anlamına gelir." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Onaylandıktan sonra, bu anahtarı kullanarak %s tarafından gönderilen " #~ "gelecek iletiler yok sayılır ve iletilerinizden hiçbiri bu anahtar " #~ "kullanılarak okunmaz." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Onaylandıktan sonra bu anahtar %s tarafından mesaj almak ve göndermek " #~ "için kullanılabilir." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Bu kişi hesaplarına yeni şifreleme anahtarları eklediğinde, otomatik " #~ "olarak kabul edilir." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Hesabınıza yeni şifreleme anahtarları eklediğinizde, bunları otomatik " #~ "olarak kabul edin." dino-0.4.3/plugins/omemo/po/uk.po0000644000000000000000000001605614452563620015356 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-12-31 17:29+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "Керування пристроями" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Скасувати" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Підтвердити" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Підтвердити ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Відбитки не збігаються" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "Відхилити ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ключ був успішно %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "" msgstr[1] "" msgstr[2] "" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "" #~ msgid "Your contact" #~ msgstr "Ваш контакт" dino-0.4.3/plugins/omemo/po/zh_CN.po0000644000000000000000000002434614452563620015741 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-03-05 11:56+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.12-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "%s 使用的是未受信任的设备。您将不会看到从您不信任的设备上发来的信息。" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "管理设备" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s 不信任该设备。这意味着您可能会错过消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "管理密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "将指纹与联系人设备上显示的指纹按逐个字符进行比较。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "指纹不匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "指纹匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "取消" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "确认" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "验证密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "之后由 %s 从使用该密钥的设备发送的消息将会相应地在聊天窗口中高亮。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "指纹不匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "请确认您正在比较正确的指纹。如果指纹不匹配,%s的帐户可能已被盗用,您应考虑拒" "绝使用此密钥。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "验证密钥指纹" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "将此密钥的指纹与联系人设备上显示的指纹进行比较。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "拒绝密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "阻止与该联络人使用该密钥的设备的加密通信。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "接受密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "允许与该联络人使用该密钥的设备的加密通信。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "此密钥当前已被%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "接受" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "这意味着%s 可以使用它来接收和发送加密消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "验证" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "此外,已验证它与联系人设备上的密钥匹配。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "拒绝" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "这意味着它不能被 %s 用于解密你的讯息,并且你将不会看到由它加密的讯息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "你将不会看到 %s 的使用该密钥的设备的加密讯息。反过来,那个设备再也不能解密你" "的讯息了。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "你将能够与 %s 的使用该密钥的设备交换加密讯息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "返回" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "此联系人有新设备" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "需要Omemo信任决策" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "是否为帐户%s添加了新设备?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "自己的指纹" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "将在第一次连接时生成" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "加密" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d 个 OMEMO 设备" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO 密钥管理" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "自动接受新密钥" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "来自该联络人的新加密密钥将被自动接受。" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "自己的密钥" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "新密钥" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "关联密钥" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "不活动的密钥" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "来自你其他设备的新加密密钥将被自动接受。" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "已接受" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "已拒绝" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "已验证" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "不再使用" #~ msgid "Your contact" #~ msgstr "您的联系人" #~ msgid "Not matching" #~ msgstr "不匹配" #~ msgid "Matching" #~ msgstr "匹配" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一旦确认,未来由%s使用此密钥发送的任何消息将在聊天窗口中相应突出显示。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "在与其关联的联系人通信期间停止接受此密钥。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "在与其关联的联系人通信期间开始接受此密钥" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "这意味着%s不能使用它来接收消息, 它发送的任何消息都将被忽略。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一旦确认,由%s使用此密钥发送的任何未来消息都将被忽略,并且使用此密钥将无法" #~ "读取任何消息。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "一旦确认,此密钥将被%s用于接收和发送消息。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "当此联系人向其帐户添加新的加密密钥时,自动接受它们。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "当您向帐户添加新的加密密钥时,会自动接受它们。" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "未知设备 (0x%.8x)" #~ msgid "Other devices" #~ msgstr "其他设备" #~ msgid "- None -" #~ msgstr "- 无 -" #~ msgid "Database error" #~ msgstr "数据库错误" dino-0.4.3/plugins/omemo/po/zh_TW.po0000644000000000000000000002441314452563620015766 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-24 05:28+0000\n" "Language-Team: Chinese (Traditional) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:157 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "%s 使用了未信任的裝置。您不會看見來自未被您信任裝置的訊息。" #: plugins/omemo/src/ui/bad_messages_populator.vala:158 msgid "Manage devices" msgstr "管理裝置" #: plugins/omemo/src/ui/bad_messages_populator.vala:160 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s 不信任這部裝置。這意味著您可能會遺漏訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "管理金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "將指紋與顯示在聯絡人裝置上的指紋逐個字元地進行比較。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "指紋不同" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "指紋吻合" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "取消" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "確定" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "核驗金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "未來由 %s 從使用了此金鑰的裝置所傳送的訊息都將會在對話視窗內加亮顯示。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "指紋不吻合" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "請確定您正在比較正確的指紋。如果指紋不吻合,%s 的帳號可能不安全,同時您應該考" "虑拒絕此金鑰。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "核驗金鑰指紋" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "將此金鑰的指紋與顯示在聯絡人裝置上的指紋進行比較。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/contact_details_dialog.vala:269 msgid "Reject key" msgstr "拒絕金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "封鎖與該聯絡人使用此金鑰的裝置之間的加密通訊。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/contact_details_dialog.vala:264 msgid "Accept key" msgstr "接收金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "允許與該聯絡人使用此金鑰的裝置之間的加密通訊。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "此金鑰目前%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "已接收" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "這表示 %s 可以使用它來傳送及收受加密訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "已核驗" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "此外,已經核驗它與聯絡人裝置上的金鑰吻合。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "已拒絕" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "這表示 %s 無法使用它來解密您的訊息,同時您不會看到使用它加密的訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "您不會看到從 %s 使用此金鑰的裝置所傳送的加密訊息。同時,該裝置亦無法再解密您" "的訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "您將能夠與 %s 所使用此金鑰的裝置互相傳送加密訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "返回" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:83 msgid "This contact has new devices" msgstr "此連絡人有新的裝置" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "需要 OMEMO 信任決策" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "您是否有爲帳號 %s 增加新裝置?" #: plugins/omemo/src/ui/account_settings_entry.vala:47 #: plugins/omemo/src/ui/account_settings_entry.vala:50 msgid "Own fingerprint" msgstr "本裝置指紋" #: plugins/omemo/src/ui/account_settings_entry.vala:47 msgid "Will be generated on first connection" msgstr "將會在第一次連線時產生" #: plugins/omemo/src/ui/contact_details_provider.vala:42 msgid "Encryption" msgstr "加密" #: plugins/omemo/src/ui/contact_details_provider.vala:42 #, c-format msgid "%d OMEMO device" msgid_plural "%d OMEMO devices" msgstr[0] "%d 個 OMEMO 裝置" #: plugins/omemo/src/ui/contact_details_dialog.vala:48 msgid "OMEMO Key Management" msgstr "OMEMO 金鑰管理" #: plugins/omemo/src/ui/contact_details_dialog.vala:49 msgid "Automatically accept new keys" msgstr "自動接收新金鑰" #: plugins/omemo/src/ui/contact_details_dialog.vala:50 msgid "New encryption keys from this contact will be accepted automatically." msgstr "新的來自此聯絡人的加密金鑰將會被自動接收。" #: plugins/omemo/src/ui/contact_details_dialog.vala:51 msgid "Own key" msgstr "本裝置金鑰" #: plugins/omemo/src/ui/contact_details_dialog.vala:52 msgid "New keys" msgstr "新金鑰" #: plugins/omemo/src/ui/contact_details_dialog.vala:53 msgid "Associated keys" msgstr "其它裝置上的金鑰" #: plugins/omemo/src/ui/contact_details_dialog.vala:54 msgid "Inactive keys" msgstr "長時間未用過的金鑰" #: plugins/omemo/src/ui/contact_details_dialog.vala:86 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "來自您其它裝置的新加密金鑰將會被自動接收。" #: plugins/omemo/src/ui/contact_details_dialog.vala:335 msgid "Accepted" msgstr "已接收" #: plugins/omemo/src/ui/contact_details_dialog.vala:340 msgid "Rejected" msgstr "已拒絕" #: plugins/omemo/src/ui/contact_details_dialog.vala:345 msgid "Verified" msgstr "已核驗" #: plugins/omemo/src/ui/contact_details_dialog.vala:352 msgid "Unused" msgstr "不再使用" #~ msgid "Your contact" #~ msgstr "您的聯絡人" #~ msgid "Not matching" #~ msgstr "不吻合" #~ msgid "Matching" #~ msgstr "吻合" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一經確定,所有未來由 %s 使用此金鑰傳送的訊息都會在對話視窗內被加亮。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "在與其連結的聯絡人通訊期間停止接收此金鑰。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "在與其連結的聯絡人通訊期間開始接收此金鑰" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "這代表 %s 不能使用它來收受訊息,並且經它傳送的所有訊息都將被忽略。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一經確定,所有未來由 %s 使用此金鑰傳送的訊息都會被忽略,並且無法使用此金鑰" #~ "讀取您的任何訊息。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "一經確定,此金鑰將會被 %s 用於收受及傳送訊息。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "當此聯絡人新增加密金鑰到其帳號時,自動接收它們。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "當您新增加密金鑰到您的帳號時,自動接收它們。" dino-0.4.3/plugins/omemo/src/0000755000000000000000000000000014452563620014540 5ustar rootrootdino-0.4.3/plugins/omemo/src/dtls_srtp_verification_draft.vala0000644000000000000000000003451414452563620023354 0ustar rootrootusing Signal; using Gee; using Xmpp; namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft { public const string NS_URI = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"; public class StreamModule : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "dtls_srtp_omemo_verification_draft"); private VerificationSendListener send_listener = new VerificationSendListener(); private HashMap device_id_by_jingle_sid = new HashMap(); private HashMap device_id_by_muji_member = new HashMap(); private HashMap> content_names_by_jingle_sid = new HashMap>(); private void on_preprocess_incoming_iq_set_get(XmppStream stream, Xmpp.Iq.Stanza iq) { if (iq.type_ != Iq.Stanza.TYPE_SET) return; Gee.List content_nodes = iq.stanza.get_deep_subnodes(Xep.Jingle.NS_URI + ":jingle", Xep.Jingle.NS_URI + ":content"); if (content_nodes.size == 0) return; string? jingle_sid = iq.stanza.get_deep_attribute(Xep.Jingle.NS_URI + ":jingle", "sid"); if (jingle_sid == null) return; Xep.Omemo.OmemoDecryptor decryptor = stream.get_module(Xep.Omemo.OmemoDecryptor.IDENTITY); foreach (StanzaNode content_node in content_nodes) { string? content_name = content_node.get_attribute("name"); if (content_name == null) continue; StanzaNode? transport_node = content_node.get_subnode("transport", Xep.JingleIceUdp.NS_URI); if (transport_node == null) continue; StanzaNode? fingerprint_node = transport_node.get_subnode("fingerprint", NS_URI); if (fingerprint_node == null) continue; StanzaNode? encrypted_node = fingerprint_node.get_subnode("encrypted", Omemo.NS_URI); if (encrypted_node == null) continue; Xep.Omemo.ParsedData? parsed_data = decryptor.parse_node(encrypted_node); if (parsed_data == null || parsed_data.ciphertext == null) continue; if (device_id_by_jingle_sid.has_key(jingle_sid) && device_id_by_jingle_sid[jingle_sid] != parsed_data.sid) { warning("Expected DTLS fingerprint to be OMEMO encrypted from %s %d, but it was from %d", iq.from.to_string(), device_id_by_jingle_sid[jingle_sid], parsed_data.sid); } foreach (Bytes encr_key in parsed_data.our_potential_encrypted_keys.keys) { parsed_data.is_prekey = parsed_data.our_potential_encrypted_keys[encr_key]; parsed_data.encrypted_key = encr_key.get_data(); try { uint8[] key = decryptor.decrypt_key(parsed_data, iq.from.bare_jid); string cleartext = decryptor.decrypt(parsed_data.ciphertext, key, parsed_data.iv); StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", Xep.JingleIceUdp.DTLS_NS_URI).add_self_xmlns() .put_node(new StanzaNode.text(cleartext)); string? hash_attr = fingerprint_node.get_attribute("hash", NS_URI); string? setup_attr = fingerprint_node.get_attribute("setup", NS_URI); if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr); if (setup_attr != null) new_fingerprint_node.put_attribute("setup", setup_attr); transport_node.put_node(new_fingerprint_node); device_id_by_jingle_sid[jingle_sid] = parsed_data.sid; if (!content_names_by_jingle_sid.has_key(content_name)) { content_names_by_jingle_sid[content_name] = new ArrayList(); } content_names_by_jingle_sid[content_name].add(content_name); stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.begin(jingle_sid, (_, res) => { Xep.Jingle.Session? session = stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.end(res); if (session == null || !session.contents_map.has_key(content_name)) return; var encryption = new OmemoContentEncryption(NS_URI, "OMEMO", iq.from.bare_jid, device_id_by_jingle_sid[jingle_sid]); session.contents_map[content_name].encryptions[NS_URI] = encryption; if (iq.stanza.get_deep_attribute(Xep.Jingle.NS_URI + ":jingle", "action") == "session-accept") { session.additional_content_add_incoming.connect(on_content_add_received); } }); break; } catch (Error e) { debug("Decrypting message from %s/%d failed: %s", iq.from.bare_jid.to_string(), parsed_data.sid, e.message); } } } } private void on_preprocess_outgoing_iq_set_get(XmppStream stream, Xmpp.Iq.Stanza iq) { if (iq.type_ != Iq.Stanza.TYPE_SET) return; StanzaNode? jingle_node = iq.stanza.get_subnode("jingle", Xep.Jingle.NS_URI); if (jingle_node == null) return; int device_id = -1; string? sid = jingle_node.get_attribute("sid", Xep.Jingle.NS_URI); if (sid != null && device_id_by_jingle_sid.has_key(sid)) { device_id = device_id_by_jingle_sid[sid]; } StanzaNode? muji_node = jingle_node.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node != null) { string muji_room = muji_node.get_attribute("room"); try { Jid muji_jid = new Jid(muji_room); if (device_id_by_muji_member.has_key(@"$(muji_jid.bare_jid)/$(iq.to)")) { device_id = device_id_by_muji_member[@"$(muji_jid.bare_jid)/$(iq.to)"]; } } catch (InvalidJidError e) { // Ignore } } if (device_id == -1) return; Gee.List content_nodes = jingle_node.get_subnodes("content", Xep.Jingle.NS_URI); if (content_nodes.size == 0) return; foreach (StanzaNode content_node in content_nodes) { StanzaNode? transport_node = content_node.get_subnode("transport", Xep.JingleIceUdp.NS_URI); if (transport_node == null) continue; StanzaNode? fingerprint_node = transport_node.get_subnode("fingerprint", Xep.JingleIceUdp.DTLS_NS_URI); if (fingerprint_node == null) continue; string fingerprint = fingerprint_node.get_deep_string_content(); StanzaNode? encrypted_node = null; try { Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY); Xep.Omemo.EncryptionData enc_data = encryptor.encrypt_plaintext(fingerprint); encryptor.encrypt_key(enc_data, iq.to.bare_jid, device_id); encrypted_node = enc_data.get_encrypted_node(); } catch (Error e) { warning("Error while OMEMO-encrypting call keys: %s", e.message); return; } StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", NS_URI).add_self_xmlns().put_node(encrypted_node); string? hash_attr = fingerprint_node.get_attribute("hash", Xep.JingleIceUdp.DTLS_NS_URI); string? setup_attr = fingerprint_node.get_attribute("setup", Xep.JingleIceUdp.DTLS_NS_URI); if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr); if (setup_attr != null) new_fingerprint_node.put_attribute("setup", setup_attr); transport_node.put_node(new_fingerprint_node); transport_node.sub_nodes.remove(fingerprint_node); } } private void on_message_received(XmppStream stream, Xmpp.MessageStanza message) { StanzaNode? proceed_node = message.stanza.get_subnode("proceed", Xep.JingleMessageInitiation.NS_URI); if (proceed_node == null) return; string? jingle_sid = proceed_node.get_attribute("id"); if (jingle_sid == null) return; StanzaNode? device_node = proceed_node.get_subnode("device", NS_URI); if (device_node == null) return; int device_id = device_node.get_attribute_int("id", -1); if (device_id == -1) return; device_id_by_jingle_sid[jingle_sid] = device_id; } private void on_session_initiate_received(XmppStream stream, Xep.Jingle.Session session) { if (device_id_by_jingle_sid.has_key(session.sid)) { foreach (Xep.Jingle.Content content in session.contents) { on_content_add_received(stream, content); } } session.additional_content_add_incoming.connect(on_content_add_received); } private void on_content_add_received(XmppStream stream, Xep.Jingle.Content content) { if (!content_names_by_jingle_sid.has_key(content.session.sid) || content_names_by_jingle_sid[content.session.sid].contains(content.content_name)) { var encryption = new OmemoContentEncryption(NS_URI, "OMEMO", content.peer_full_jid.bare_jid, device_id_by_jingle_sid[content.session.sid]); content.encryptions[encryption.encryption_ns] = encryption; } } private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { StanzaNode? muji_node = presence.stanza.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node == null) return; StanzaNode device_node = new StanzaNode.build("device", NS_URI).add_self_xmlns() .put_attribute("id", stream.get_module(Omemo.StreamModule.IDENTITY).store.local_registration_id.to_string()); muji_node.put_node(device_node); } private void on_received_available(XmppStream stream, Presence.Stanza presence) { StanzaNode? muji_node = presence.stanza.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node == null) return; StanzaNode? device_node = muji_node.get_subnode("device", NS_URI); if (device_node == null) return; int device_id = device_node.get_attribute_int("id", -1); if (device_id == -1) return; StanzaNode? muc_x_node = presence.stanza.get_subnode("x", "http://jabber.org/protocol/muc#user"); if (muc_x_node == null) return; StanzaNode? item_node = muc_x_node.get_subnode("item"); if (item_node == null) return; Jid? real_jid = null; try { string jid_attribute = item_node.get_attribute("jid"); if (jid_attribute == null) return; real_jid = new Jid(jid_attribute); } catch (InvalidJidError e) { // Ignore return; } device_id_by_muji_member[@"$(presence.from.bare_jid)/$(real_jid)"] = device_id; } public override void attach(XmppStream stream) { stream.get_module(Xmpp.MessageModule.IDENTITY).received_message.connect(on_message_received); stream.get_module(Xmpp.MessageModule.IDENTITY).send_pipeline.connect(send_listener); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_incoming_iq_set_get.connect(on_preprocess_incoming_iq_set_get); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_outgoing_iq_set_get.connect(on_preprocess_outgoing_iq_set_get); stream.get_module(Xep.Jingle.Module.IDENTITY).session_initiate_received.connect(on_session_initiate_received); stream.get_module(Xmpp.Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); stream.get_module(Xmpp.Presence.Module.IDENTITY).received_available.connect(on_received_available); } public override void detach(XmppStream stream) { stream.get_module(Xmpp.MessageModule.IDENTITY).received_message.disconnect(on_message_received); stream.get_module(Xmpp.MessageModule.IDENTITY).send_pipeline.disconnect(send_listener); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_incoming_iq_set_get.disconnect(on_preprocess_incoming_iq_set_get); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_outgoing_iq_set_get.disconnect(on_preprocess_outgoing_iq_set_get); stream.get_module(Xep.Jingle.Module.IDENTITY).session_initiate_received.disconnect(on_session_initiate_received); stream.get_module(Xmpp.Presence.Module.IDENTITY).received_available.disconnect(on_received_available); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class VerificationSendListener : StanzaListener { private string[] after_actions_const = {}; public override string action_group { get { return "REWRITE_NODES"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(XmppStream stream, MessageStanza message) { StanzaNode? proceed_node = message.stanza.get_subnode("proceed", Xep.JingleMessageInitiation.NS_URI); if (proceed_node == null) return false; StanzaNode device_node = new StanzaNode.build("device", NS_URI).add_self_xmlns() .put_attribute("id", stream.get_module(Omemo.StreamModule.IDENTITY).store.local_registration_id.to_string()); proceed_node.put_node(device_node); return false; } } public class OmemoContentEncryption : Xep.Jingle.ContentEncryption { public Jid jid { get; set; } public int sid { get; set; } public OmemoContentEncryption(string encryption_ns, string encryption_name, Jid jid, int sid) { base(encryption_ns, encryption_name); this.jid = jid; this.sid = sid; } } } dino-0.4.3/plugins/omemo/src/file_transfer/0000755000000000000000000000000014452563620017363 5ustar rootrootdino-0.4.3/plugins/omemo/src/file_transfer/file_decryptor.vala0000644000000000000000000000727714452563620023257 0ustar rootrootusing Dino.Entities; using Crypto; using Signal; namespace Dino.Plugins.Omemo { public class OmemoHttpFileReceiveData : HttpFileReceiveData { public string original_url; } public class OmemoFileDecryptor : FileDecryptor, Object { private Regex url_regex = /^aesgcm:\/\/(.*)#(([A-Fa-f0-9]{2}){48}|([A-Fa-f0-9]{2}){44})$/; public Encryption get_encryption() { return Encryption.OMEMO; } public FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) assert(false); if ((receive_data as OmemoHttpFileReceiveData) != null) return receive_data; var omemo_http_receive_data = new OmemoHttpFileReceiveData(); omemo_http_receive_data.url = aesgcm_to_https_link(http_receive_data.url); omemo_http_receive_data.original_url = http_receive_data.url; return omemo_http_receive_data; } public FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { if (file_meta.file_name != null) { file_meta.file_name = file_meta.file_name.split("#")[0]; } return file_meta; } public bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { HttpFileReceiveData? http_file_receive = receive_data as HttpFileReceiveData; if (http_file_receive == null) return false; return this.url_regex.match(http_file_receive.url) || (receive_data as OmemoHttpFileReceiveData) != null; } public async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError { const uint KEY_SIZE = 32; try { OmemoHttpFileReceiveData? omemo_http_receive_data = receive_data as OmemoHttpFileReceiveData; if (omemo_http_receive_data == null) assert(false); // Decode IV and key MatchInfo match_info; this.url_regex.match(omemo_http_receive_data.original_url, 0, out match_info); uint8[] iv_and_key = hex_to_bin(match_info.fetch(2).up()); uint8[] iv = iv_and_key[0:iv_and_key.length-KEY_SIZE]; uint8[] key = iv_and_key[iv_and_key.length-KEY_SIZE:iv_and_key.length]; file_transfer.encryption = Encryption.OMEMO; debug("Decrypting file %s from %s", file_transfer.file_name, file_transfer.server_file_name); SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(key); cipher.set_iv(iv); return new ConverterInputStream(encrypted_stream, new SymmetricCipherDecrypter((owned) cipher, 16)); } catch (Crypto.Error e) { throw new FileReceiveError.DECRYPTION_FAILED("OMEMO file decryption error: %s".printf(e.message)); } catch (GLib.Error e) { throw new FileReceiveError.DECRYPTION_FAILED("OMEMO file decryption error: %s".printf(e.message)); } } private uint8[] hex_to_bin(string hex) { uint8[] bin = new uint8[hex.length / 2]; const string HEX = "0123456789ABCDEF"; for (int i = 0; i < hex.length / 2; i++) { bin[i] = (uint8) (HEX.index_of_char(hex[i*2]) << 4) | HEX.index_of_char(hex[i*2+1]); } return bin; } private string aesgcm_to_https_link(string aesgcm_link) { MatchInfo match_info; this.url_regex.match(aesgcm_link, 0, out match_info); return "https://" + match_info.fetch(1); } } } dino-0.4.3/plugins/omemo/src/file_transfer/file_encryptor.vala0000644000000000000000000000545014452563620023260 0ustar rootrootusing Gee; using Gtk; using Crypto; using Dino.Entities; using Xmpp; using Signal; namespace Dino.Plugins.Omemo { public class OmemoHttpFileMeta : HttpFileMeta { public uint8[] iv; public uint8[] key; } public class OmemoFileEncryptor : Dino.FileEncryptor, Object { public bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer) { return file_transfer.encryption == Encryption.OMEMO; } public FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError { const uint KEY_SIZE = 32; const uint IV_SIZE = 12; var omemo_http_file_meta = new OmemoHttpFileMeta(); try { //Create a key and use it to encrypt the file uint8[] iv = new uint8[IV_SIZE]; Plugin.get_context().randomize(iv); uint8[] key = new uint8[KEY_SIZE]; Plugin.get_context().randomize(key); SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(key); cipher.set_iv(iv); omemo_http_file_meta.iv = iv; omemo_http_file_meta.key = key; omemo_http_file_meta.size = file_transfer.size + 16; omemo_http_file_meta.mime_type = "application/octet-stream"; file_transfer.input_stream = new ConverterInputStream(file_transfer.input_stream, new SymmetricCipherEncrypter((owned) cipher, 16)); } catch (Crypto.Error error) { throw new FileSendError.ENCRYPTION_FAILED("OMEMO file encryption error: %s".printf(error.message)); } catch (GLib.Error error) { throw new FileSendError.ENCRYPTION_FAILED("OMEMO file encryption error: %s".printf(error.message)); } debug("Encrypting file %s as %s", file_transfer.file_name, file_transfer.server_file_name); return omemo_http_file_meta; } public FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return null; OmemoHttpFileMeta? omemo_http_file_meta = file_meta as OmemoHttpFileMeta; if (omemo_http_file_meta == null) return null; // Convert iv and key to hex string iv_and_key = ""; foreach (uint8 byte in omemo_http_file_meta.iv) iv_and_key += byte.to_string("%02x"); foreach (uint8 byte in omemo_http_file_meta.key) iv_and_key += byte.to_string("%02x"); string aesgcm_link = send_data.url_down + "#" + iv_and_key; aesgcm_link = "aesgcm://" + aesgcm_link.substring(8); // replace https:// by aesgcm:// send_data.url_down = aesgcm_link; send_data.encrypt_message = true; return file_send_data; } } } dino-0.4.3/plugins/omemo/src/jingle/0000755000000000000000000000000014452563620016010 5ustar rootrootdino-0.4.3/plugins/omemo/src/jingle/jet_omemo.vala0000644000000000000000000001216214452563620020635 0ustar rootrootusing Crypto; using Dino; using Dino.Entities; using Gee; using Signal; using Xmpp; using Xmpp.Xep; namespace Dino.Plugins.JetOmemo { private const string NS_URI = "urn:xmpp:jingle:jet-omemo:0"; private const string AES_128_GCM_URI = "urn:xmpp:ciphers:aes-128-gcm-nopadding"; public class Module : XmppStreamModule, Jet.EnvelopEncoding { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "0396_jet_omemo"); const uint KEY_SIZE = 16; const uint IV_SIZE = 12; public override void attach(XmppStream stream) { if (stream.get_module(Jet.Module.IDENTITY) != null) { stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); stream.get_module(Jet.Module.IDENTITY).register_envelop_encoding(this); stream.get_module(Jet.Module.IDENTITY).register_cipher(new AesGcmCipher(KEY_SIZE, IV_SIZE, AES_128_GCM_URI)); } } public override void detach(XmppStream stream) { stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); } public async bool is_available(XmppStream stream, Jid full_jid) { bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); if (has_feature == null || !(!)has_feature) { return false; } return yield stream.get_module(Xep.Jet.Module.IDENTITY).is_available(stream, full_jid); } public string get_type_uri() { return Omemo.NS_URI; } public Jet.TransportSecret decode_envolop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, StanzaNode security) throws Jingle.IqError { StanzaNode? encrypted = security.get_subnode("encrypted", Omemo.NS_URI); if (encrypted == null) throw new Jingle.IqError.BAD_REQUEST("Invalid JET-OMEMO envelop: missing encrypted element"); Xep.Omemo.OmemoDecryptor decryptor = stream.get_module(Xep.Omemo.OmemoDecryptor.IDENTITY); Xmpp.Xep.Omemo.ParsedData? data = decryptor.parse_node(encrypted); if (data == null) throw new Jingle.IqError.BAD_REQUEST("Invalid JET-OMEMO envelop: bad encrypted element"); foreach (Bytes encr_key in data.our_potential_encrypted_keys.keys) { data.is_prekey = data.our_potential_encrypted_keys[encr_key]; data.encrypted_key = encr_key.get_data(); try { uint8[] key = decryptor.decrypt_key(data, peer_full_jid.bare_jid); return new Jet.TransportSecret(key, data.iv); } catch (GLib.Error e) { debug("Decrypting JET key from %s/%d failed: %s", peer_full_jid.bare_jid.to_string(), data.sid, e.message); } } throw new Jingle.IqError.NOT_ACCEPTABLE("Not encrypted for targeted device"); } public void encode_envelop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, Jet.SecurityParameters security_params, StanzaNode security) { Store store = stream.get_module(Omemo.StreamModule.IDENTITY).store; var encryption_data = new Xep.Omemo.EncryptionData(store.local_registration_id); encryption_data.iv = security_params.secret.initialization_vector; encryption_data.keytag = security_params.secret.transport_key; Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY); encryptor.encrypt_key_to_recipient(stream, encryption_data, peer_full_jid.bare_jid); security.put_node(encryption_data.get_encrypted_node()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class AesGcmCipher : Jet.Cipher, Object { private uint key_size; private uint default_iv_size; private string uri; public AesGcmCipher(uint key_size, uint default_iv_size, string uri) { this.key_size = key_size; this.default_iv_size = default_iv_size; this.uri = uri; } public string get_cipher_uri() { return uri; } public Jet.TransportSecret generate_random_secret() { uint8[] iv = new uint8[default_iv_size]; Omemo.Plugin.get_context().randomize(iv); uint8[] key = new uint8[key_size]; Omemo.Plugin.get_context().randomize(key); return new Jet.TransportSecret(key, iv); } public InputStream wrap_input_stream(InputStream input, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) { SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(secret.transport_key); cipher.set_iv(secret.initialization_vector); return new ConverterInputStream(input, new SymmetricCipherDecrypter((owned) cipher, 16)); } public OutputStream wrap_output_stream(OutputStream output, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) { Crypto.SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(secret.transport_key); cipher.set_iv(secret.initialization_vector); return new ConverterOutputStream(output, new SymmetricCipherEncrypter((owned) cipher, 16)); } } } dino-0.4.3/plugins/omemo/src/jingle/jingle_helper.vala0000644000000000000000000000356114452563620021471 0ustar rootrootusing Dino.Entities; using Xmpp; namespace Dino.Plugins.JetOmemo { public class EncryptionHelper : JingleFileEncryptionHelper, Object { private StreamInteractor stream_interactor; public EncryptionHelper(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool can_transfer(Conversation conversation) { return true; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; Gee.List? resources = stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart); if (resources == null) return false; if (full_jid == null) { foreach (Jid test_jid in resources) { if (yield stream.get_module(Module.IDENTITY).is_available(stream, test_jid)) { return true; } } } else { if (yield stream.get_module(Module.IDENTITY).is_available(stream, full_jid)) { return true; } } return false; } public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) { return Xep.Jet.NS_URI; } public Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer) { return new Xep.Jet.Options(Omemo.NS_URI, AES_128_GCM_URI); } public Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer) { Xep.Jet.SecurityParameters? security = jingle_transfer.security as Xep.Jet.SecurityParameters; if (security != null && security.encoding.get_type_uri() == Omemo.NS_URI) { return Encryption.OMEMO; } return Encryption.NONE; } } } dino-0.4.3/plugins/omemo/src/logic/0000755000000000000000000000000014452563620015635 5ustar rootrootdino-0.4.3/plugins/omemo/src/logic/database.vala0000644000000000000000000003361614452563620020257 0ustar rootrootusing Gee; using Qlite; using Dino.Entities; namespace Dino.Plugins.Omemo { public class Database : Qlite.Database { private const int VERSION = 5; public class IdentityMetaTable : Table { //Default to provide backwards compatability public Column identity_id = new Column.Integer("identity_id") { not_null = true, min_version = 2, default = "-1" }; public Column address_name = new Column.Text("address_name") { not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column identity_key_public_base64 = new Column.Text("identity_key_public_base64"); public Column trusted_identity = new Column.BoolInt("trusted_identity") { default = "0", max_version = 1 }; public Column trust_level = new Column.Integer("trust_level") { default = TrustLevel.UNKNOWN.to_string(), min_version = 2 }; public Column now_active = new Column.BoolInt("now_active") { default = "1" }; public Column last_active = new Column.Long("last_active"); public Column last_message_untrusted = new Column.Long("last_message_untrusted") { min_version = 5 }; public Column last_message_undecryptable = new Column.Long("last_message_undecryptable") { min_version = 5 }; internal IdentityMetaTable(Database db) { base(db, "identity_meta"); init({identity_id, address_name, device_id, identity_key_public_base64, trusted_identity, trust_level, now_active, last_active, last_message_untrusted, last_message_undecryptable}); index("identity_meta_idx", {identity_id, address_name, device_id}, true); index("identity_meta_list_idx", {identity_id, address_name}); } public QueryBuilder with_address(int identity_id, string address_name) { return select().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name); } public QueryBuilder get_with_device_id(int identity_id, int device_id) { return select().with(this.identity_id, "=", identity_id).with(this.device_id, "=", device_id); } public void insert_device_list(int32 identity_id, string address_name, ArrayList devices) { update().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name).set(now_active, false).perform(); foreach (int32 device_id in devices) { upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.now_active, true) .value(this.last_active, (long) new DateTime.now_utc().to_unix()) .perform(); } } public int64 insert_device_bundle(int32 identity_id, string address_name, int device_id, Bundle bundle, TrustLevel trust) { if (bundle == null || bundle.identity_key == null) return -1; // Do not replace identity_key if it was known before, it should never change! string identity_key = Base64.encode(bundle.identity_key.serialize()); RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row(); if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) { critical("Tried to change the identity key for a known device id. Likely an attack."); return -1; } return upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.identity_key_public_base64, identity_key) .value(this.trust_level, trust).perform(); } public int64 insert_device_session(int32 identity_id, string address_name, int device_id, string identity_key, TrustLevel trust) { RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row(); if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) { critical("Tried to change the identity key for a known device id. Likely an attack."); return -1; } return upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.identity_key_public_base64, identity_key) .value(this.trust_level, trust).perform(); } public void update_last_message_untrusted(int identity_id, int device_id, DateTime? time) { var stmt = update() .with(this.identity_id, "=", identity_id) .with(this.device_id, "=", device_id); if (time != null) { stmt.set(last_message_untrusted, (long)time.to_unix()); } else { stmt.set_null(last_message_untrusted); } stmt.perform(); } public void update_last_message_undecryptable(int identity_id, int device_id, DateTime? time) { var stmt = update() .with(this.identity_id, "=", identity_id) .with(this.device_id, "=", device_id); if (time != null) { stmt.set(last_message_undecryptable, (long)time.to_unix()); } else { stmt.set_null(last_message_undecryptable); } stmt.perform(); } public QueryBuilder get_trusted_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "!=", TrustLevel.UNTRUSTED) .with(this.now_active, "=", true); } public QueryBuilder get_known_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "!=", TrustLevel.UNKNOWN) .without_null(this.identity_key_public_base64); } public QueryBuilder get_unknown_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with_null(this.identity_key_public_base64); } public QueryBuilder get_new_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "=", TrustLevel.UNKNOWN) .without_null(this.identity_key_public_base64); } public Row? get_device(int identity_id, string address_name, int device_id) { return this.with_address(identity_id, address_name) .with(this.device_id, "=", device_id).single().row().inner; } } public class TrustTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.Text("address_name"); public Column blind_trust = new Column.BoolInt("blind_trust") { default = "1" } ; internal TrustTable(Database db) { base(db, "trust"); init({identity_id, address_name, blind_trust}); index("trust_idx", {identity_id, address_name}, true); } public bool get_blind_trust(int32 identity_id, string address_name, bool def = false) { RowOption row = this.select().with(this.identity_id, "=", identity_id) .with(this.address_name, "=", address_name).single().row(); if (row.is_present()) return row[blind_trust]; return def; } } public class IdentityTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { unique = true, not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column identity_key_private_base64 = new Column.NonNullText("identity_key_private_base64"); public Column identity_key_public_base64 = new Column.NonNullText("identity_key_public_base64"); internal IdentityTable(Database db) { base(db, "identity"); init({id, account_id, device_id, identity_key_private_base64, identity_key_public_base64}); } public int get_id(int account_id) { int id = -1; Row? row = this.row_with(this.account_id, account_id).inner; if (row != null) id = ((!)row)[this.id]; return id; } } public class SignedPreKeyTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column signed_pre_key_id = new Column.Integer("signed_pre_key_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal SignedPreKeyTable(Database db) { base(db, "signed_pre_key"); init({identity_id, signed_pre_key_id, record_base64}); unique({identity_id, signed_pre_key_id}); index("signed_pre_key_idx", {identity_id, signed_pre_key_id}, true); } } public class PreKeyTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column pre_key_id = new Column.Integer("pre_key_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal PreKeyTable(Database db) { base(db, "pre_key"); init({identity_id, pre_key_id, record_base64}); unique({identity_id, pre_key_id}); index("pre_key_idx", {identity_id, pre_key_id}, true); } } public class SessionTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.NonNullText("name"); public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal SessionTable(Database db) { base(db, "session"); init({identity_id, address_name, device_id, record_base64}); unique({identity_id, address_name, device_id}); index("session_idx", {identity_id, address_name, device_id}, true); } } public class ContentItemMetaTable : Table { public Column content_item_id = new Column.Integer("message_id") { primary_key = true }; public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.Text("address_name") { not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column trusted_when_received = new Column.BoolInt("trusted_when_received") { not_null = true, default = "1" }; internal ContentItemMetaTable(Database db) { base(db, "content_item_meta"); init({content_item_id, identity_id, address_name, device_id, trusted_when_received}); index("content_item_meta_device_idx", {identity_id, device_id, address_name}); } public RowOption with_content_item(ContentItem item) { return row_with(content_item_id, item.id); } public QueryBuilder with_device(int identity_id, string address_name, int device_id) { return select() .with(this.identity_id, "=", identity_id) .with(this.address_name, "=", address_name) .with(this.device_id, "=", device_id); } } public IdentityMetaTable identity_meta { get; private set; } public TrustTable trust { get; private set; } public IdentityTable identity { get; private set; } public SignedPreKeyTable signed_pre_key { get; private set; } public PreKeyTable pre_key { get; private set; } public SessionTable session { get; private set; } public ContentItemMetaTable content_item_meta { get; private set; } public Database(string fileName) { base(fileName, VERSION); identity_meta = new IdentityMetaTable(this); trust = new TrustTable(this); identity = new IdentityTable(this); signed_pre_key = new SignedPreKeyTable(this); pre_key = new PreKeyTable(this); session = new SessionTable(this); content_item_meta = new ContentItemMetaTable(this); init({identity_meta, trust, identity, signed_pre_key, pre_key, session, content_item_meta}); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set OMEMO database properties: %s", e.message); } } public override void migrate(long oldVersion) { if(oldVersion == 1) { try { exec("DROP INDEX identity_meta_idx"); exec("DROP INDEX identity_meta_list_idx"); exec("CREATE UNIQUE INDEX identity_meta_idx ON identity_meta (identity_id, address_name, device_id)"); exec("CREATE INDEX identity_meta_list_idx ON identity_meta (identity_id, address_name)"); } catch (Error e) { stderr.printf("Failed to migrate OMEMO database\n"); Process.exit(-1); } } } } } dino-0.4.3/plugins/omemo/src/logic/decrypt.vala0000644000000000000000000002456114452563620020164 0ustar rootrootusing Dino.Entities; using Qlite; using Gee; using Signal; using Xmpp; namespace Dino.Plugins.Omemo { public class OmemoDecryptor : Xep.Omemo.OmemoDecryptor { private Account account; private Store store; private Database db; private StreamInteractor stream_interactor; private TrustManager trust_manager; public override uint32 own_device_id { get { return store.local_registration_id; }} public OmemoDecryptor(Account account, StreamInteractor stream_interactor, TrustManager trust_manager, Database db, Store store) { this.account = account; this.stream_interactor = stream_interactor; this.trust_manager = trust_manager; this.db = db; this.store = store; } public bool decrypt_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { StanzaNode? encrypted_node = stanza.stanza.get_subnode("encrypted", NS_URI); if (encrypted_node == null || MessageFlag.get_flag(stanza) != null || stanza.from == null) return false; if (message.body == null && Xep.ExplicitEncryption.get_encryption_tag(stanza) == NS_URI) { message.body = "[This message is OMEMO encrypted]"; // TODO temporary } if (!Plugin.ensure_context()) return false; int identity_id = db.identity.get_id(conversation.account.id); MessageFlag flag = new MessageFlag(); stanza.add_flag(flag); Xep.Omemo.ParsedData? data = parse_node(encrypted_node); if (data == null || data.ciphertext == null) return false; foreach (Bytes encr_key in data.our_potential_encrypted_keys.keys) { data.is_prekey = data.our_potential_encrypted_keys[encr_key]; data.encrypted_key = encr_key.get_data(); Gee.List possible_jids = get_potential_message_jids(message, data, identity_id); if (possible_jids.size == 0) { debug("Received message from unknown entity with device id %d", data.sid); } foreach (Jid possible_jid in possible_jids) { try { uint8[] key = decrypt_key(data, possible_jid); string cleartext = arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, data.iv, data.ciphertext)); // If we figured out which real jid a message comes from due to decryption working, save it if (conversation.type_ == Conversation.Type.GROUPCHAT && message.real_jid == null) { message.real_jid = possible_jid; } message.body = cleartext; message.encryption = Encryption.OMEMO; trust_manager.message_device_id_map[message] = data.sid; return true; } catch (Error e) { debug("Decrypting message from %s/%d failed: %s", possible_jid.to_string(), data.sid, e.message); } } } if ( encrypted_node.get_deep_string_content("payload") != null && // Ratchet forwarding doesn't contain payload and might not include us, which is ok data.our_potential_encrypted_keys.size == 0 && // The message was not encrypted to us stream_interactor.module_manager.get_module(message.account, StreamModule.IDENTITY).store.local_registration_id != data.sid // Message from this device. Never encrypted to itself. ) { db.identity_meta.update_last_message_undecryptable(identity_id, data.sid, message.time); trust_manager.bad_message_state_updated(conversation.account, message.from, data.sid); } debug("Received OMEMO encryped message that could not be decrypted."); return false; } public Gee.List get_potential_message_jids(Entities.Message message, Xmpp.Xep.Omemo.ParsedData data, int identity_id) { Gee.List possible_jids = new ArrayList(); if (message.type_ == Message.Type.CHAT) { possible_jids.add(message.from.bare_jid); } else { if (message.real_jid != null) { possible_jids.add(message.real_jid.bare_jid); } else if (data.is_prekey) { // pre key messages do store the identity key, so we can use that to find the real jid PreKeySignalMessage msg = Plugin.get_context().deserialize_pre_key_signal_message(data.encrypted_key); string identity_key = Base64.encode(msg.identity_key.serialize()); foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid).with(db.identity_meta.identity_key_public_base64, "=", identity_key)) { try { possible_jids.add(new Jid(row[db.identity_meta.address_name])); } catch (InvalidJidError e) { warning("Ignoring invalid jid from database: %s", e.message); } } } else { // If we don't know the device name (MUC history w/o MAM), test decryption with all keys with fitting device id foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid)) { try { possible_jids.add(new Jid(row[db.identity_meta.address_name])); } catch (InvalidJidError e) { warning("Ignoring invalid jid from database: %s", e.message); } } } } return possible_jids; } public override uint8[] decrypt_key(Xmpp.Xep.Omemo.ParsedData data, Jid from_jid) throws GLib.Error { int sid = data.sid; uint8[] ciphertext = data.ciphertext; uint8[] encrypted_key = data.encrypted_key; Address address = new Address(from_jid.to_string(), sid); uint8[] key; if (data.is_prekey) { int identity_id = db.identity.get_id(account.id); PreKeySignalMessage msg = Plugin.get_context().deserialize_pre_key_signal_message(encrypted_key); string identity_key = Base64.encode(msg.identity_key.serialize()); bool ok = update_db_for_prekey(identity_id, identity_key, from_jid, sid); if (!ok) throw new GLib.Error(-1, 0, "Failed updating db for prekey"); debug("Starting new session for decryption with device from %s/%d", from_jid.to_string(), sid); SessionCipher cipher = store.create_session_cipher(address); key = cipher.decrypt_pre_key_signal_message(msg); // TODO: Finish session } else { debug("Continuing session for decryption with device from %s/%d", from_jid.to_string(), sid); SignalMessage msg = Plugin.get_context().deserialize_signal_message(encrypted_key); SessionCipher cipher = store.create_session_cipher(address); key = cipher.decrypt_signal_message(msg); } if (key.length >= 32) { int authtaglength = key.length - 16; uint8[] new_ciphertext = new uint8[ciphertext.length + authtaglength]; uint8[] new_key = new uint8[16]; Memory.copy(new_ciphertext, ciphertext, ciphertext.length); Memory.copy((uint8*)new_ciphertext + ciphertext.length, (uint8*)key + 16, authtaglength); Memory.copy(new_key, key, 16); data.ciphertext = new_ciphertext; key = new_key; } return key; } public override string decrypt(uint8[] ciphertext, uint8[] key, uint8[] iv) throws GLib.Error { return arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, iv, ciphertext)); } private bool update_db_for_prekey(int identity_id, string identity_key, Jid from_jid, int sid) { Row? device = db.identity_meta.get_device(identity_id, from_jid.to_string(), sid); if (device != null && device[db.identity_meta.identity_key_public_base64] != null) { if (device[db.identity_meta.identity_key_public_base64] != identity_key) { critical("Tried to use a different identity key for a known device id."); return false; } } else { debug("Learn new device from incoming message from %s/%d", from_jid.to_string(), sid); bool blind_trust = db.trust.get_blind_trust(identity_id, from_jid.to_string(), true); if (db.identity_meta.insert_device_session(identity_id, from_jid.to_string(), sid, identity_key, blind_trust ? TrustLevel.TRUSTED : TrustLevel.UNKNOWN) < 0) { critical("Failed learning a device."); return false; } XmppStream? stream = stream_interactor.get_stream(account); if (device == null && stream != null) { stream.get_module(StreamModule.IDENTITY).request_user_devicelist.begin(stream, from_jid); } } return true; } private string arr_to_str(uint8[] arr) { // null-terminate the array uint8[] rarr = new uint8[arr.length+1]; Memory.copy(rarr, arr, arr.length); return (string)rarr; } } public class DecryptMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "DECRYPT"; } } public override string[] after_actions { get { return after_actions_const; } } private HashMap decryptors; public DecryptMessageListener(HashMap decryptors) { this.decryptors = decryptors; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { decryptors[message.account].decrypt_message(message, stanza, conversation); return false; } } } dino-0.4.3/plugins/omemo/src/logic/encrypt.vala0000644000000000000000000001322014452563620020164 0ustar rootrootusing Gee; using Signal; using Dino.Entities; using Xmpp; using Xmpp.Xep.Omemo; namespace Dino.Plugins.Omemo { public class OmemoEncryptor : Xep.Omemo.OmemoEncryptor { private Account account; private Store store; private TrustManager trust_manager; public override uint32 own_device_id { get { return store.local_registration_id; }} public OmemoEncryptor(Account account, TrustManager trust_manager, Store store) { this.account = account; this.trust_manager = trust_manager; this.store = store; } public override Xep.Omemo.EncryptionData encrypt_plaintext(string plaintext) throws GLib.Error { const uint KEY_SIZE = 16; const uint IV_SIZE = 12; //Create a key and use it to encrypt the message uint8[] key = new uint8[KEY_SIZE]; Plugin.get_context().randomize(key); uint8[] iv = new uint8[IV_SIZE]; Plugin.get_context().randomize(iv); uint8[] aes_encrypt_result = aes_encrypt(Cipher.AES_GCM_NOPADDING, key, iv, plaintext.data); uint8[] ciphertext = aes_encrypt_result[0:aes_encrypt_result.length - 16]; uint8[] tag = aes_encrypt_result[aes_encrypt_result.length - 16:aes_encrypt_result.length]; uint8[] keytag = new uint8[key.length + tag.length]; Memory.copy(keytag, key, key.length); Memory.copy((uint8*)keytag + key.length, tag, tag.length); var ret = new Xep.Omemo.EncryptionData(own_device_id); ret.ciphertext = ciphertext; ret.keytag = keytag; ret.iv = iv; return ret; } public EncryptState encrypt(MessageStanza message, Jid self_jid, Gee.List recipients, XmppStream stream) { EncryptState status = new EncryptState(); if (!Plugin.ensure_context()) return status; if (message.to == null) return status; try { EncryptionData enc_data = encrypt_plaintext(message.body); status = encrypt_key_to_recipients(enc_data, self_jid, recipients, stream); message.stanza.put_node(enc_data.get_encrypted_node()); Xep.ExplicitEncryption.add_encryption_tag_to_message(message, NS_URI, "OMEMO"); message.body = "[This message is OMEMO encrypted]"; status.encrypted = true; } catch (Error e) { warning(@"Signal error while encrypting message: $(e.message)\n"); message.body = "[OMEMO encryption failed]"; status.encrypted = false; } return status; } internal EncryptState encrypt_key_to_recipients(EncryptionData enc_data, Jid self_jid, Gee.List recipients, XmppStream stream) throws Error { EncryptState status = new EncryptState(); //Check we have the bundles and device lists needed to send the message if (!trust_manager.is_known_address(account, self_jid)) return status; status.own_list = true; status.own_devices = trust_manager.get_trusted_devices(account, self_jid).size; status.other_waiting_lists = 0; status.other_devices = 0; foreach (Jid recipient in recipients) { if (!trust_manager.is_known_address(account, recipient)) { status.other_waiting_lists++; } if (status.other_waiting_lists > 0) return status; status.other_devices += trust_manager.get_trusted_devices(account, recipient).size; } if (status.own_devices == 0 || status.other_devices == 0) return status; //Encrypt the key for each recipient's device individually foreach (Jid recipient in recipients) { EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, recipient); status.add_result(enc_res, false); } // Encrypt the key for each own device EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, self_jid); status.add_result(enc_res, true); return status; } public override EncryptionResult encrypt_key_to_recipient(XmppStream stream, Xep.Omemo.EncryptionData enc_data, Jid recipient) throws GLib.Error { var result = new EncryptionResult(); StreamModule module = stream.get_module(StreamModule.IDENTITY); foreach(int32 device_id in trust_manager.get_trusted_devices(account, recipient)) { if (module.is_ignored_device(recipient, device_id)) { result.lost++; continue; } try { encrypt_key(enc_data, recipient, device_id); result.success++; } catch (Error e) { if (e.code == ErrorCode.UNKNOWN) result.unknown++; else result.failure++; } } return result; } public override void encrypt_key(Xep.Omemo.EncryptionData encryption_data, Jid jid, int32 device_id) throws GLib.Error { Address address = new Address(jid.to_string(), device_id); SessionCipher cipher = store.create_session_cipher(address); CiphertextMessage device_key = cipher.encrypt(encryption_data.keytag); address.device_id = 0; debug("Created encrypted key for %s/%d", jid.to_string(), device_id); encryption_data.add_device_key(device_id, device_key.serialized, device_key.type == CiphertextType.PREKEY); } } }dino-0.4.3/plugins/omemo/src/logic/manager.vala0000644000000000000000000004777314452563620020136 0ustar rootrootusing Dino.Entities; using Signal; using Qlite; using Xmpp; using Gee; namespace Dino.Plugins.Omemo { public class Manager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("omemo_manager"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private TrustManager trust_manager; private HashMap encryptors; private Map message_states = new HashMap(Entities.Message.hash_func, Entities.Message.equals_func); private class MessageState { public Entities.Message msg { get; private set; } public Xep.Omemo.EncryptState last_try { get; private set; } public int waiting_other_sessions { get; set; } public int waiting_own_sessions { get; set; } public bool waiting_own_devicelist { get; set; } public int waiting_other_devicelists { get; set; } public bool force_next_attempt { get; set; } public bool will_send_now { get; private set; } public bool active_send_attempt { get; set; } public MessageState(Entities.Message msg, Xep.Omemo.EncryptState last_try) { update_from_encrypt_status(msg, last_try); } public void update_from_encrypt_status(Entities.Message msg, Xep.Omemo.EncryptState new_try) { this.msg = msg; this.last_try = new_try; this.waiting_other_sessions = new_try.other_unknown; this.waiting_own_sessions = new_try.own_unknown; this.waiting_own_devicelist = !new_try.own_list; this.waiting_other_devicelists = new_try.other_waiting_lists; this.active_send_attempt = false; will_send_now = false; if (new_try.other_failure > 0 || (new_try.other_lost == new_try.other_devices && new_try.other_devices > 0)) { msg.marked = Entities.Message.Marked.WONTSEND; } else if (new_try.other_unknown > 0 || new_try.own_unknown > 0 || new_try.other_waiting_lists > 0 || !new_try.own_list || new_try.own_devices == 0) { msg.marked = Entities.Message.Marked.UNSENT; } else if (!new_try.encrypted) { msg.marked = Entities.Message.Marked.WONTSEND; } else { will_send_now = true; } } public bool should_retry_now() { return !waiting_own_devicelist && waiting_other_devicelists <= 0 && waiting_other_sessions <= 0 && waiting_own_sessions <= 0 && !active_send_attempt; } public string to_string() { return @"MessageState (msg=$(msg.stanza_id), send=$will_send_now, $last_try)"; } } private Manager(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap encryptors) { this.stream_interactor = stream_interactor; this.db = db; this.trust_manager = trust_manager; this.encryptors = encryptors; stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_send.connect(on_pre_message_send); stream_interactor.get_module(RosterManager.IDENTITY).mutual_subscription.connect(on_mutual_subscription); } public void clear_device_list(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; stream.get_module(StreamModule.IDENTITY).clear_device_list(stream); } private Gee.List get_occupants(Jid jid, Account account){ Gee.List occupants = new ArrayList(Jid.equals_bare_func); if(!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(jid, account)){ occupants.add(jid); } Gee.List? occupant_jids = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(jid, account); if(occupant_jids == null) { return occupants; } foreach (Jid occupant in occupant_jids) { if(!occupant.equals(account.bare_jid)){ occupants.add(occupant.bare_jid); } } return occupants; } private void on_pre_message_send(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (message.encryption == Encryption.OMEMO) { if (message.type_ == Message.Type.GROUPCHAT_PM) { message.marked = Message.Marked.WONTSEND; return; } XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) { message.marked = Entities.Message.Marked.UNSENT; return; } StreamModule? module_ = ((!)stream).get_module(StreamModule.IDENTITY); if (module_ == null) { message.marked = Entities.Message.Marked.UNSENT; return; } StreamModule module = (!)module_; //Get a list of everyone for whom the message should be encrypted Gee.List recipients; if (message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { recipients = get_occupants((!)message.to.bare_jid, conversation.account); if (recipients.size == 0) { message.marked = Entities.Message.Marked.WONTSEND; return; } } else { recipients = new ArrayList(Jid.equals_bare_func); recipients.add(message_stanza.to); } //Attempt to encrypt the message Xep.Omemo.EncryptState enc_state = encryptors[conversation.account].encrypt(message_stanza, conversation.account.bare_jid, recipients, stream); MessageState state; lock (message_states) { if (message_states.has_key(message)) { state = message_states.get(message); state.update_from_encrypt_status(message, enc_state); if (state.will_send_now) { debug("sending message delayed: %s", state.to_string()); } } else { state = new MessageState(message, enc_state); message_states[message] = state; } if (state.will_send_now) { message_states.unset(message); } } //Encryption failed - need to fetch more information if (!state.will_send_now) { if (message.marked == Entities.Message.Marked.WONTSEND) { debug("retracting message %s", state.to_string()); message_states.unset(message); } else { debug("delaying message %s", state.to_string()); if (state.waiting_own_sessions > 0) { module.fetch_bundles((!)stream, conversation.account.bare_jid, trust_manager.get_trusted_devices(conversation.account, conversation.account.bare_jid)); } if (state.waiting_other_sessions > 0 && message.counterpart != null) { foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) { module.fetch_bundles((!)stream, jid, trust_manager.get_trusted_devices(conversation.account, jid)); } } if (state.waiting_other_devicelists > 0 && message.counterpart != null) { foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) { module.request_user_devicelist.begin((!)stream, jid); } } } } } } private void on_mutual_subscription(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if(stream == null) return; stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist.begin((!)stream, jid); } private void on_stream_negotiated(Account account, XmppStream stream) { StreamModule module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY); if (module != null) { module.request_user_devicelist.begin(stream, account.bare_jid); module.device_list_loaded.connect((jid, devices) => on_device_list_loaded(account, jid, devices)); module.bundle_fetched.connect((jid, device_id, bundle) => on_bundle_fetched(account, jid, device_id, bundle)); module.bundle_fetch_failed.connect((jid) => continue_message_sending(account, jid)); } initialize_store.begin(account); } private void on_device_list_loaded(Account account, Jid jid, ArrayList device_list) { debug("received device list for %s from %s", account.bare_jid.to_string(), jid.to_string()); XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) { return; } StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY); if (module == null) { return; } int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; //Update meta database db.identity_meta.insert_device_list(identity_id, jid.bare_jid.to_string(), device_list); //Fetch the bundle for each new device int inc = 0; foreach (Row row in db.identity_meta.get_unknown_devices(identity_id, jid.bare_jid.to_string())) { try { module.fetch_bundle(stream, new Jid(row[db.identity_meta.address_name]), row[db.identity_meta.device_id], false); inc++; } catch (InvalidJidError e) { warning("Ignoring device with invalid Jid: %s", e.message); } } if (inc > 0) { debug("new bundles %i/%i for %s", inc, device_list.size, jid.to_string()); } //Create an entry for the jid in the account table if one does not exist already if (db.trust.select().with(db.trust.identity_id, "=", identity_id).with(db.trust.address_name, "=", jid.bare_jid.to_string()).count() == 0) { db.trust.insert().value(db.trust.identity_id, identity_id).value(db.trust.address_name, jid.bare_jid.to_string()).value(db.trust.blind_trust, true).perform(); } //Get all messages that needed the devicelist and determine if we can now send them HashSet send_now = new HashSet(); lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); MessageState state = message_states[msg]; if (account.bare_jid.equals(jid)) { state.waiting_own_devicelist = false; } else if (msg.counterpart != null && occupants.contains(jid)) { state.waiting_other_devicelists--; } if (state.should_retry_now()) { send_now.add(msg); state.active_send_attempt = true; } } } foreach (Entities.Message msg in send_now) { if (msg.counterpart == null) continue; Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(((!)msg.counterpart), account); if (conv == null) continue; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true); } } private void on_bundle_fetched(Account account, Jid jid, int32 device_id, Bundle bundle) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; bool blind_trust = db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true); //If we don't blindly trust new devices and we haven't seen this key before then don't trust it bool untrust = !(blind_trust || db.identity_meta.with_address(identity_id, jid.bare_jid.to_string()) .with(db.identity_meta.device_id, "=", device_id) .with(db.identity_meta.identity_key_public_base64, "=", Base64.encode(bundle.identity_key.serialize())) .single().row().is_present()); //Get trust information from the database if the device id is known Row device = db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id); TrustLevel trusted = TrustLevel.UNKNOWN; if (device != null) { trusted = (TrustLevel) device[db.identity_meta.trust_level]; } if(untrust) { trusted = TrustLevel.UNKNOWN; } else if (blind_trust && trusted == TrustLevel.UNKNOWN) { trusted = TrustLevel.TRUSTED; } //Update the database with the appropriate trust information db.identity_meta.insert_device_bundle(identity_id, jid.bare_jid.to_string(), device_id, bundle, trusted); if (should_start_session(account, jid)) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY); if (module != null) { module.start_session(stream, jid, device_id, bundle); } } } continue_message_sending(account, jid); } private bool should_start_session(Account account, Jid jid) { lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); if (account.bare_jid.equals(jid) || (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid)))) { return true; } } } return false; } private void continue_message_sending(Account account, Jid jid) { //Get all messages waiting and determine if they can now be sent HashSet send_now = new HashSet(); lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); MessageState state = message_states[msg]; if (account.bare_jid.equals(jid)) { state.waiting_own_sessions--; } else if (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid))) { state.waiting_other_sessions--; } if (state.should_retry_now()){ send_now.add(msg); state.active_send_attempt = true; } } } foreach (Entities.Message msg in send_now) { if (msg.counterpart == null) continue; Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation((!)msg.counterpart, account); if (conv == null) continue; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true); } } private async void initialize_store(Account account) { // If the account is not yet persisted, wait for that and then continue - without identity.account_id the entry isn't worth much. if (account.id == -1) { account.notify["id"].connect(() => initialize_store.callback()); yield; } StreamModule? module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY); if (module == null) return; Store store = module.store; Qlite.Row? row = db.identity.row_with(db.identity.account_id, account.id).inner; int identity_id = -1; bool publish_identity = false; if (row == null) { // OMEMO not yet initialized, starting with empty base publish_identity = true; try { store.identity_key_store.local_registration_id = Random.int_range(1, int32.MAX); Signal.ECKeyPair key_pair = Plugin.get_context().generate_key_pair(); store.identity_key_store.identity_key_private = new Bytes(key_pair.private.serialize()); store.identity_key_store.identity_key_public = new Bytes(key_pair.public.serialize()); identity_id = (int) db.identity.upsert() .value(db.identity.account_id, account.id, true) .value(db.identity.device_id, (int) store.local_registration_id) .value(db.identity.identity_key_private_base64, Base64.encode(store.identity_key_store.identity_key_private.get_data())) .value(db.identity.identity_key_public_base64, Base64.encode(store.identity_key_store.identity_key_public.get_data())) .perform(); } catch (Error e) { // Ignore error } } else { store.identity_key_store.local_registration_id = ((!)row)[db.identity.device_id]; store.identity_key_store.identity_key_private = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_private_base64])); store.identity_key_store.identity_key_public = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_public_base64])); identity_id = ((!)row)[db.identity.id]; } if (identity_id >= 0) { store.signed_pre_key_store = new BackedSignedPreKeyStore(db, identity_id); store.pre_key_store = new BackedPreKeyStore(db, identity_id); store.session_store = new BackedSessionStore(db, identity_id); } else { warning("store for %s is not persisted!", account.bare_jid.to_string()); } // Generated new device ID, ensure this gets added to the devicelist XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { module.request_user_devicelist.begin((!)stream, account.bare_jid); } } public async bool ensure_get_keys_for_conversation(Conversation conversation) { if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart)) { foreach (Jid offline_member in stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account)) { bool ok = yield ensure_get_keys_for_jid(conversation.account, offline_member); if (!ok) { return false; } } return true; } return yield ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid); } public async bool ensure_get_keys_for_jid(Account account, Jid jid) { if (trust_manager.is_known_address(account, jid)) return true; XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { var device_list = yield stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist(stream, jid); return device_list.size > 0; } return true; // TODO wait for stream? } public static void start(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap encryptors) { Manager m = new Manager(stream_interactor, db, trust_manager, encryptors); stream_interactor.add_module(m); } } } dino-0.4.3/plugins/omemo/src/logic/pre_key_store.vala0000644000000000000000000000256114452563620021360 0ustar rootrootusing Signal; using Qlite; namespace Dino.Plugins.Omemo { private class BackedPreKeyStore : SimplePreKeyStore { private Database db; private int identity_id; public BackedPreKeyStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.pre_key.select().with(db.pre_key.identity_id, "=", identity_id)) { store_pre_key(row[db.pre_key.pre_key_id], Base64.decode(row[db.pre_key.record_base64])); } } catch (Error e) { warning("Error while initializing pre key store: %s", e.message); } pre_key_stored.connect(on_pre_key_stored); pre_key_deleted.connect(on_pre_key_deleted); } public void on_pre_key_stored(PreKeyStore.Key key) { db.pre_key.upsert() .value(db.pre_key.identity_id, identity_id, true) .value(db.pre_key.pre_key_id, (int) key.key_id, true) .value(db.pre_key.record_base64, Base64.encode(key.record)) .perform(); } public void on_pre_key_deleted(PreKeyStore.Key key) { db.pre_key.delete() .with(db.pre_key.identity_id, "=", identity_id) .with(db.pre_key.pre_key_id, "=", (int) key.key_id) .perform(); } } } dino-0.4.3/plugins/omemo/src/logic/session_store.vala0000644000000000000000000000320114452563620021375 0ustar rootrootusing Signal; using Qlite; namespace Dino.Plugins.Omemo { private class BackedSessionStore : SimpleSessionStore { private Database db; private int identity_id; public BackedSessionStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.session.select().with(db.session.identity_id, "=", identity_id)) { Address addr = new Address(row[db.session.address_name], row[db.session.device_id]); store_session(addr, Base64.decode(row[db.session.record_base64])); addr.device_id = 0; } } catch (Error e) { print("Error while initializing session store: %s", e.message); } session_stored.connect(on_session_stored); session_removed.connect(on_session_deleted); } public void on_session_stored(SessionStore.Session session) { db.session.upsert() .value(db.session.identity_id, identity_id, true) .value(db.session.address_name, session.name, true) .value(db.session.device_id, session.device_id, true) .value(db.session.record_base64, Base64.encode(session.record)) .perform(); } public void on_session_deleted(SessionStore.Session session) { db.session.delete() .with(db.session.identity_id, "=", identity_id) .with(db.session.address_name, "=", session.name) .with(db.session.device_id, "=", session.device_id) .perform(); } } } dino-0.4.3/plugins/omemo/src/logic/signed_pre_key_store.vala0000644000000000000000000000304714452563620022711 0ustar rootrootusing Qlite; using Signal; namespace Dino.Plugins.Omemo { private class BackedSignedPreKeyStore : SimpleSignedPreKeyStore { private Database db; private int identity_id; public BackedSignedPreKeyStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.signed_pre_key.select().with(db.signed_pre_key.identity_id, "=", identity_id)) { store_signed_pre_key(row[db.signed_pre_key.signed_pre_key_id], Base64.decode(row[db.signed_pre_key.record_base64])); } } catch (Error e) { print("Error while initializing signed pre key store: %s", e.message); } signed_pre_key_stored.connect(on_signed_pre_key_stored); signed_pre_key_deleted.connect(on_signed_pre_key_deleted); } public void on_signed_pre_key_stored(SignedPreKeyStore.Key key) { db.signed_pre_key.upsert() .value(db.signed_pre_key.identity_id, identity_id, true) .value(db.signed_pre_key.signed_pre_key_id, (int) key.key_id, true) .value(db.signed_pre_key.record_base64, Base64.encode(key.record)) .perform(); } public void on_signed_pre_key_deleted(SignedPreKeyStore.Key key) { db.signed_pre_key.delete() .with(db.signed_pre_key.identity_id, "=", identity_id) .with(db.signed_pre_key.signed_pre_key_id, "=", (int) key.key_id) .perform(); } } } dino-0.4.3/plugins/omemo/src/logic/trust_manager.vala0000644000000000000000000001472514452563620021366 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; using Signal; using Qlite; namespace Dino.Plugins.Omemo { public class TrustManager { public signal void bad_message_state_updated(Account account, Jid jid, int device_id); private StreamInteractor stream_interactor; private Database db; private TagMessageListener tag_message_listener; public HashMap message_device_id_map = new HashMap(Message.hash_func, Message.equals_func); public TrustManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; tag_message_listener = new TagMessageListener(stream_interactor, this, db, message_device_id_map); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(tag_message_listener); } public void set_blind_trust(Account account, Jid jid, bool blind_trust) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; db.trust.update() .with(db.trust.identity_id, "=", identity_id) .with(db.trust.address_name, "=", jid.bare_jid.to_string()) .set(db.trust.blind_trust, blind_trust).perform(); } public void set_device_trust(Account account, Jid jid, int device_id, TrustLevel trust_level) { int identity_id = db.identity.get_id(account.id); db.identity_meta.update() .with(db.identity_meta.identity_id, "=", identity_id) .with(db.identity_meta.address_name, "=", jid.bare_jid.to_string()) .with(db.identity_meta.device_id, "=", device_id) .set(db.identity_meta.trust_level, trust_level).perform(); // Hide messages from untrusted or unknown devices string selection = null; string[] selection_args = {}; var app_db = Application.get_default().db; foreach (Row row in db.content_item_meta.with_device(identity_id, jid.bare_jid.to_string(), device_id).with(db.content_item_meta.trusted_when_received, "=", false)) { if (selection == null) { selection = @"$(app_db.content_item.id) = ?"; } else { selection += @" OR $(app_db.content_item.id) = ?"; } selection_args += row[db.content_item_meta.content_item_id].to_string(); } if (selection != null) { app_db.content_item.update() .set(app_db.content_item.hide, trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) .where(selection, selection_args) .perform(); } if (trust_level == TrustLevel.TRUSTED) { db.identity_meta.update_last_message_untrusted(identity_id, device_id, null); bad_message_state_updated(account, jid, device_id); } } public bool is_known_address(Account account, Jid jid) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return false; return db.identity_meta.with_address(identity_id, jid.to_string()).with(db.identity_meta.last_active, ">", 0).count() > 0; } public Gee.List get_trusted_devices(Account account, Jid jid) { Gee.List devices = new ArrayList(); int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return devices; foreach (Row device in db.identity_meta.get_trusted_devices(identity_id, jid.bare_jid.to_string())) { if(device[db.identity_meta.trust_level] != TrustLevel.UNKNOWN || device[db.identity_meta.identity_key_public_base64] == null) devices.add(device[db.identity_meta.device_id]); } return devices; } private class TagMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "DECRYPT_TAG"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; private TrustManager trust_manager; private Database db; private HashMap message_device_id_map; public TagMessageListener(StreamInteractor stream_interactor, TrustManager trust_manager, Database db, HashMap message_device_id_map) { this.stream_interactor = stream_interactor; this.trust_manager = trust_manager; this.db = db; this.message_device_id_map = message_device_id_map; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { int device_id = 0; if (message_device_id_map.has_key(message)) { device_id = message_device_id_map[message]; message_device_id_map.unset(message); } // TODO: Handling of files ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); if (content_item != null && device_id != 0) { Jid jid = content_item.jid; if (conversation.type_ == Conversation.Type.GROUPCHAT) { jid = message.real_jid; } int identity_id = db.identity.get_id(conversation.account.id); TrustLevel trust_level = (TrustLevel) db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id)[db.identity_meta.trust_level]; if (trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) { stream_interactor.get_module(ContentItemStore.IDENTITY).set_item_hide(content_item, true); db.identity_meta.update_last_message_untrusted(identity_id, device_id, message.time); trust_manager.bad_message_state_updated(conversation.account, jid, device_id); } db.content_item_meta.insert() .value(db.content_item_meta.content_item_id, content_item.id) .value(db.content_item_meta.identity_id, identity_id) .value(db.content_item_meta.address_name, jid.bare_jid.to_string()) .value(db.content_item_meta.device_id, device_id) .value(db.content_item_meta.trusted_when_received, trust_level != TrustLevel.UNTRUSTED) .perform(); } return false; } } } } dino-0.4.3/plugins/omemo/src/plugin.vala0000644000000000000000000001220214452563620016700 0ustar rootrootusing Gee; using Dino.Entities; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.Omemo { public class Plugin : RootInterface, Object { public const bool DEBUG = false; private static Signal.Context? _context; public static Signal.Context get_context() { assert(_context != null); return (!)_context; } public static bool ensure_context() { lock(_context) { try { if (_context == null) { _context = new Signal.Context(DEBUG); } return true; } catch (Error e) { warning("Error initializing Signal Context %s", e.message); return false; } } } public Dino.Application app; public Database db; public EncryptionListEntry list_entry; public AccountSettingsEntry settings_entry; public ContactDetailsProvider contact_details_provider; public DeviceNotificationPopulator device_notification_populator; public OwnNotifications own_notifications; public TrustManager trust_manager; public HashMap decryptors = new HashMap(Account.hash_func, Account.equals_func); public HashMap encryptors = new HashMap(Account.hash_func, Account.equals_func); public void registered(Dino.Application app) { ensure_context(); this.app = app; this.db = new Database(Path.build_filename(Application.get_storage_dir(), "omemo.db")); this.list_entry = new EncryptionListEntry(this); this.settings_entry = new AccountSettingsEntry(this); this.contact_details_provider = new ContactDetailsProvider(this); this.device_notification_populator = new DeviceNotificationPopulator(this, this.app.stream_interactor); this.trust_manager = new TrustManager(this.app.stream_interactor, this.db); this.app.plugin_registry.register_encryption_list_entry(list_entry); this.app.plugin_registry.register_account_settings_entry(settings_entry); this.app.plugin_registry.register_contact_details_entry(contact_details_provider); this.app.plugin_registry.register_notification_populator(device_notification_populator); this.app.plugin_registry.register_conversation_addition_populator(new BadMessagesPopulator(this.app.stream_interactor, this)); this.app.plugin_registry.register_call_entryption_entry(DtlsSrtpVerificationDraft.NS_URI, new CallEncryptionEntry(db)); this.app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { Signal.Store signal_store = Plugin.get_context().create_store(); list.add(new StreamModule(signal_store)); decryptors[account] = new OmemoDecryptor(account, app.stream_interactor, trust_manager, db, signal_store); list.add(decryptors[account]); encryptors[account] = new OmemoEncryptor(account, trust_manager,signal_store); list.add(encryptors[account]); list.add(new JetOmemo.Module()); list.add(new DtlsSrtpVerificationDraft.StreamModule()); this.own_notifications = new OwnNotifications(this, this.app.stream_interactor, account); }); app.stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new DecryptMessageListener(decryptors)); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_decryptor(new OmemoFileDecryptor()); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_encryptor(new OmemoFileEncryptor()); JingleFileHelperRegistry.instance.add_encryption_helper(Encryption.OMEMO, new JetOmemo.EncryptionHelper(app.stream_interactor)); Manager.start(this.app.stream_interactor, db, trust_manager, encryptors); SimpleAction own_keys_action = new SimpleAction("own-keys", VariantType.INT32); own_keys_action.activate.connect((variant) => { foreach(Dino.Entities.Account account in this.app.stream_interactor.get_accounts()) { if(account.id == variant.get_int32()) { ContactDetailsDialog dialog = new ContactDetailsDialog(this, account, account.bare_jid); dialog.set_transient_for(((Gtk.Application) this.app).get_active_window()); dialog.present(); } } }); this.app.add_action(own_keys_action); string locales_dir; if (app.search_path_generator != null) { locales_dir = ((!)app.search_path_generator).get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR); } else { locales_dir = LOCALE_INSTALL_DIR; } internationalize(GETTEXT_PACKAGE, locales_dir); } public void shutdown() { // Nothing to do } public bool has_new_devices(Account account, Xmpp.Jid jid) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return false; return db.identity_meta.get_new_devices(identity_id, jid.bare_jid.to_string()).count() > 0; } } } dino-0.4.3/plugins/omemo/src/protocol/0000755000000000000000000000000014452563620016401 5ustar rootrootdino-0.4.3/plugins/omemo/src/protocol/bundle.vala0000644000000000000000000000515514452563620020525 0ustar rootrootusing Gee; using Signal; using Xmpp; namespace Dino.Plugins.Omemo { public class Bundle { public StanzaNode? node; public Bundle(StanzaNode? node) { this.node = node; assert(Plugin.ensure_context()); } public int32 signed_pre_key_id { owned get { if (node == null) return -1; string? id = ((!)node).get_deep_attribute("signedPreKeyPublic", "signedPreKeyId"); if (id == null) return -1; return int.parse((!)id); }} public ECPublicKey? signed_pre_key { owned get { if (node == null) return null; string? key = ((!)node).get_deep_string_content("signedPreKeyPublic"); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} public uint8[]? signed_pre_key_signature { owned get { if (node == null) return null; string? sig = ((!)node).get_deep_string_content("signedPreKeySignature"); if (sig == null) return null; return Base64.decode((!)sig); }} public ECPublicKey? identity_key { owned get { if (node == null) return null; string? key = ((!)node).get_deep_string_content("identityKey"); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} public ArrayList pre_keys { owned get { ArrayList list = new ArrayList(); if (node == null || ((!)node).get_subnode("prekeys") == null) return list; ((!)node).get_deep_subnodes("prekeys", "preKeyPublic") .filter((node) => ((!)node).get_attribute("preKeyId") != null) .map(PreKey.create) .foreach((key) => list.add(key)); return list; }} public class PreKey { private StanzaNode node; public static PreKey create(owned StanzaNode node) { return new PreKey(node); } public PreKey(StanzaNode node) { this.node = node; } public int32 key_id { owned get { return int.parse(node.get_attribute("preKeyId") ?? "-1"); }} public ECPublicKey? key { owned get { string? key = node.get_string_content(); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} } } } dino-0.4.3/plugins/omemo/src/protocol/message_flag.vala0000644000000000000000000000066014452563620021665 0ustar rootrootusing Xmpp; namespace Dino.Plugins.Omemo { public class MessageFlag : Xmpp.MessageFlag { public const string id = "omemo"; public bool decrypted = false; public static MessageFlag? get_flag(MessageStanza message) { return (MessageFlag) message.get_flag(NS_URI, id); } public override string get_ns() { return NS_URI; } public override string get_id() { return id; } } }dino-0.4.3/plugins/omemo/src/protocol/stream_module.vala0000644000000000000000000003660014452563620022113 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Signal; namespace Dino.Plugins.Omemo { internal const string NS_URI = "eu.siacs.conversations.axolotl"; private const string NODE_DEVICELIST = NS_URI + ".devicelist"; private const string NODE_BUNDLES = NS_URI + ".bundles"; private const string NODE_VERIFICATION = NS_URI + ".verification"; private const int NUM_KEYS_TO_PUBLISH = 100; public class StreamModule : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "omemo_module"); private static TimeSpan IGNORE_TIME = TimeSpan.MINUTE; public Store store { public get; private set; } private ConcurrentSet active_bundle_requests = new ConcurrentSet(); private HashMap>> active_devicelist_requests = new HashMap>>(Jid.hash_func, Jid.equals_func); private Map device_ignore_time = new HashMap(); public signal void device_list_loaded(Jid jid, ArrayList devices); public signal void bundle_fetched(Jid jid, int device_id, Bundle bundle); public signal void bundle_fetch_failed(Jid jid, int device_id); public StreamModule(Store store) { this.store = store; } public override void attach(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NODE_DEVICELIST, true, (stream, jid, id, node) => parse_device_list(stream, jid, id, node), null); } public override void detach(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NODE_DEVICELIST); } public async ArrayList request_user_devicelist(XmppStream stream, Jid jid) { var future = active_devicelist_requests[jid]; if (future == null) { var promise = new Promise?>(); future = promise.future; active_devicelist_requests[jid] = future; stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid, NODE_DEVICELIST, (stream, jid, id, node) => { ArrayList device_list = parse_device_list(stream, jid, id, node); promise.set_value(device_list); active_devicelist_requests.unset(jid); }); } try { ArrayList device_list = yield future.wait_async(); return device_list; } catch (FutureError error) { warning("Future error when waiting for device list: %s", error.message); return new ArrayList(); } } public ArrayList parse_device_list(XmppStream stream, Jid jid, string? id, StanzaNode? node_) { ArrayList device_list = new ArrayList(); StanzaNode node = node_ ?? new StanzaNode.build("list", NS_URI).add_self_xmlns(); Jid? my_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid; if (my_jid == null) return device_list; if (jid.equals_bare(my_jid) && store.local_registration_id != 0) { bool am_on_devicelist = false; foreach (StanzaNode device_node in node.get_subnodes("device")) { int device_id = device_node.get_attribute_int("id"); if (store.local_registration_id == device_id) { am_on_devicelist = true; } } if (!am_on_devicelist) { debug("Not on device list, adding id"); node.put_node(new StanzaNode.build("device", NS_URI).put_attribute("id", store.local_registration_id.to_string())); stream.get_module(Pubsub.Module.IDENTITY).publish.begin(stream, jid, NODE_DEVICELIST, id, node, null, true, () => { try_make_node_public.begin(stream, NODE_DEVICELIST); }); } publish_bundles_if_needed(stream, jid); } foreach (StanzaNode device_node in node.get_subnodes("device")) { device_list.add(device_node.get_attribute_int("id")); } device_list_loaded(jid, device_list); return device_list; } public void fetch_bundles(XmppStream stream, Jid jid, Gee.List devices) { Address address = new Address(jid.bare_jid.to_string(), 0); foreach(int32 device_id in devices) { if (!is_ignored_device(jid, device_id)) { address.device_id = device_id; try { if (!store.contains_session(address)) { fetch_bundle(stream, jid, device_id); } } catch (Error e) { // Ignore } } } address.device_id = 0; // TODO: Hack to have address obj live longer } public void fetch_bundle(XmppStream stream, Jid jid, int device_id, bool ignore_if_non_present = true) { if (active_bundle_requests.add(jid.bare_jid.to_string() + @":$device_id")) { debug("Asking for bundle for %s/%d", jid.bare_jid.to_string(), device_id); stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid.bare_jid, @"$NODE_BUNDLES:$device_id", (stream, jid, id, node) => { on_other_bundle_result(stream, jid, device_id, id, node, ignore_if_non_present); }); } } public void ignore_device(Jid jid, int32 device_id) { if (device_id <= 0) return; lock (device_ignore_time) { device_ignore_time[jid.bare_jid.to_string() + @":$device_id"] = new DateTime.now_utc(); } } public void unignore_device(Jid jid, int32 device_id) { if (device_id <= 0) return; lock (device_ignore_time) { device_ignore_time.unset(jid.bare_jid.to_string() + @":$device_id"); } } public bool is_ignored_device(Jid jid, int32 device_id) { if (device_id <= 0) return true; lock (device_ignore_time) { string id = jid.bare_jid.to_string() + @":$device_id"; if (device_ignore_time.has_key(id)) { return new DateTime.now_utc().difference(device_ignore_time[id]) < IGNORE_TIME; } } return false; } public void clear_device_list(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).delete_node(stream, null, NODE_DEVICELIST); } private void on_other_bundle_result(XmppStream stream, Jid jid, int device_id, string? id, StanzaNode? node, bool ignore_if_non_present) { if (node == null) { // Device not registered, shouldn't exist if (ignore_if_non_present) { debug("Ignoring device %s/%d: No bundle", jid.bare_jid.to_string(), device_id); stream.get_module(IDENTITY).ignore_device(jid, device_id); } bundle_fetch_failed(jid, device_id); } else { Bundle bundle = new Bundle(node); stream.get_module(IDENTITY).unignore_device(jid, device_id); debug("Received bundle for %s/%d: %s", jid.bare_jid.to_string(), device_id, Base64.encode(bundle.identity_key.serialize())); bundle_fetched(jid, device_id, bundle); } stream.get_module(IDENTITY).active_bundle_requests.remove(jid.bare_jid.to_string() + @":$device_id"); } public bool start_session(XmppStream stream, Jid jid, int32 device_id, Bundle bundle) { bool fail = false; int32 signed_pre_key_id = bundle.signed_pre_key_id; ECPublicKey? signed_pre_key = bundle.signed_pre_key; uint8[] signed_pre_key_signature = bundle.signed_pre_key_signature; ECPublicKey? identity_key = bundle.identity_key; ArrayList pre_keys = bundle.pre_keys; if (signed_pre_key_id < 0 || signed_pre_key == null || identity_key == null || pre_keys.size == 0) { fail = true; } else { int pre_key_idx = Random.int_range(0, pre_keys.size); int32 pre_key_id = pre_keys[pre_key_idx].key_id; ECPublicKey? pre_key = pre_keys[pre_key_idx].key; if (pre_key_id < 0 || pre_key == null) { fail = true; } else { Address address = new Address(jid.bare_jid.to_string(), device_id); try { if (store.contains_session(address)) { return false; } debug("Starting new session for encryption with %s/%d", jid.bare_jid.to_string(), device_id); SessionBuilder builder = store.create_session_builder(address); builder.process_pre_key_bundle(create_pre_key_bundle(device_id, device_id, pre_key_id, pre_key, signed_pre_key_id, signed_pre_key, signed_pre_key_signature, identity_key)); } catch (Error e) { debug("Can't create session with %s/%d: %s", jid.bare_jid.to_string(), device_id, e.message); fail = true; } address.device_id = 0; // TODO: Hack to have address obj live longer } } if (fail) { debug("Ignoring device %s/%d: Bad bundle: %s", jid.bare_jid.to_string(), device_id, bundle.node.to_string()); stream.get_module(IDENTITY).ignore_device(jid, device_id); } return true; } public void publish_bundles_if_needed(XmppStream stream, Jid jid) { if (active_bundle_requests.add(jid.bare_jid.to_string() + @":$(store.local_registration_id)")) { stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid, @"$NODE_BUNDLES:$(store.local_registration_id)", on_self_bundle_result); } } private void on_self_bundle_result(XmppStream stream, Jid jid, string? id, StanzaNode? node) { if (!Plugin.ensure_context()) return; Map keys = new HashMap(); ECPublicKey? identity_key = null; int32 signed_pre_key_id = -1; ECPublicKey? signed_pre_key = null; SignedPreKeyRecord? signed_pre_key_record = null; bool changed = false; if (node == null) { identity_key = store.identity_key_pair.public; changed = true; } else { Bundle bundle = new Bundle(node); foreach (Bundle.PreKey prekey in bundle.pre_keys) { ECPublicKey? key = prekey.key; if (key != null) { keys[prekey.key_id] = (!)key; } } identity_key = bundle.identity_key; signed_pre_key_id = bundle.signed_pre_key_id; signed_pre_key = bundle.signed_pre_key; } try { // Validate IdentityKey if (identity_key == null || store.identity_key_pair.public.compare((!)identity_key) != 0) { changed = true; } IdentityKeyPair identity_key_pair = store.identity_key_pair; // Validate signedPreKeyRecord + ID if (signed_pre_key == null || signed_pre_key_id == -1 || !store.contains_signed_pre_key(signed_pre_key_id) || store.load_signed_pre_key(signed_pre_key_id).key_pair.public.compare((!)signed_pre_key) != 0) { signed_pre_key_id = Random.int_range(1, int32.MAX); // TODO: No random, use ordered number signed_pre_key_record = Plugin.get_context().generate_signed_pre_key(identity_key_pair, signed_pre_key_id); store.store_signed_pre_key((!)signed_pre_key_record); changed = true; } else { signed_pre_key_record = store.load_signed_pre_key(signed_pre_key_id); } // Validate PreKeys Set pre_key_records = new HashSet(); foreach (var entry in keys.entries) { if (store.contains_pre_key(entry.key)) { PreKeyRecord record = store.load_pre_key(entry.key); if (record.key_pair.public.compare(entry.value) == 0) { pre_key_records.add(record); } } } int new_keys = NUM_KEYS_TO_PUBLISH - pre_key_records.size; if (new_keys > 0) { int32 next_id = Random.int_range(1, int32.MAX); // TODO: No random, use ordered number Set new_records = Plugin.get_context().generate_pre_keys((uint)next_id, (uint)new_keys); pre_key_records.add_all(new_records); foreach (PreKeyRecord record in new_records) { store.store_pre_key(record); } changed = true; } if (changed) { publish_bundles.begin(stream, (!)signed_pre_key_record, identity_key_pair, pre_key_records, (int32) store.local_registration_id); } } catch (Error e) { warning(@"Unexpected error while publishing bundle: $(e.message)\n"); } stream.get_module(IDENTITY).active_bundle_requests.remove(jid.bare_jid.to_string() + @":$(store.local_registration_id)"); } public async void publish_bundles(XmppStream stream, SignedPreKeyRecord signed_pre_key_record, IdentityKeyPair identity_key_pair, Set pre_key_records, int32 device_id) throws Error { ECKeyPair tmp; StanzaNode bundle = new StanzaNode.build("bundle", NS_URI) .add_self_xmlns() .put_node(new StanzaNode.build("signedPreKeyPublic", NS_URI) .put_attribute("signedPreKeyId", signed_pre_key_record.id.to_string()) .put_node(new StanzaNode.text(Base64.encode((tmp = signed_pre_key_record.key_pair).public.serialize())))) .put_node(new StanzaNode.build("signedPreKeySignature", NS_URI) .put_node(new StanzaNode.text(Base64.encode(signed_pre_key_record.signature)))) .put_node(new StanzaNode.build("identityKey", NS_URI) .put_node(new StanzaNode.text(Base64.encode(identity_key_pair.public.serialize())))); StanzaNode prekeys = new StanzaNode.build("prekeys", NS_URI); foreach (PreKeyRecord pre_key_record in pre_key_records) { prekeys.put_node(new StanzaNode.build("preKeyPublic", NS_URI) .put_attribute("preKeyId", pre_key_record.id.to_string()) .put_node(new StanzaNode.text(Base64.encode(pre_key_record.key_pair.public.serialize())))); } bundle.put_node(prekeys); string node_id = @"$NODE_BUNDLES:$device_id"; yield stream.get_module(Pubsub.Module.IDENTITY).publish(stream, null, node_id, "1", bundle); yield try_make_node_public(stream, node_id); } private async void try_make_node_public(XmppStream stream, string node_id) { DataForms.DataForm? data_form = yield stream.get_module(Pubsub.Module.IDENTITY).request_node_config(stream, null, node_id); if (data_form == null) return; foreach (DataForms.DataForm.Field field in data_form.fields) { if (field.var == "pubsub#access_model" && field.get_value_string() != Pubsub.ACCESS_MODEL_OPEN) { field.set_value_string(Pubsub.ACCESS_MODEL_OPEN); yield stream.get_module(Pubsub.Module.IDENTITY).submit_node_config(stream, data_form, node_id); break; } } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/plugins/omemo/src/register_plugin.vala0000644000000000000000000000013614452563620020607 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Omemo.Plugin); } dino-0.4.3/plugins/omemo/src/trust_level.vala0000644000000000000000000000032414452563620017754 0ustar rootrootnamespace Dino.Plugins.Omemo { public enum TrustLevel { VERIFIED, TRUSTED, UNTRUSTED, UNKNOWN; public string to_string() { int val = this; return val.to_string(); } } } dino-0.4.3/plugins/omemo/src/ui/0000755000000000000000000000000014452563620015155 5ustar rootrootdino-0.4.3/plugins/omemo/src/ui/account_settings_entry.vala0000644000000000000000000000410214452563620022614 0ustar rootrootusing Dino.Entities; using Gtk; namespace Dino.Plugins.Omemo { public class AccountSettingsEntry : Plugins.AccountSettingsEntry { private Plugin plugin; private Account account; private Box box = new Box(Orientation.HORIZONTAL, 0); private Label fingerprint = new Label("...") { xalign=0 }; private Button btn = new Button.from_icon_name("view-list-symbolic") { has_frame=false, valign=Align.CENTER, visible=false }; public override string id { get { return "omemo_identity_key"; }} public override string name { get { return "OMEMO"; }} public AccountSettingsEntry(Plugin plugin) { this.plugin = plugin; Border border = new Button().get_style_context().get_padding(); fingerprint.margin_top = border.top + 1; fingerprint.margin_start = border.left + 1; fingerprint.visible = true; box.append(fingerprint); btn.clicked.connect(() => { activated(); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, account.bare_jid); dialog.set_transient_for((Window) box.get_root()); dialog.present(); }); // TODO expand=false? box.append(btn); } public override Object? get_widget(WidgetType type) { if (type != WidgetType.GTK4) return null; return box; } public override void set_account(Account account) { this.account = account; btn.visible = false; Qlite.Row? row = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id).inner; if (row == null) { fingerprint.set_markup("%s\n%s".printf(_("Own fingerprint"), _("Will be generated on first connection"))); } else { string res = fingerprint_markup(fingerprint_from_base64(((!)row)[plugin.db.identity.identity_key_public_base64])); fingerprint.set_markup("%s\n%s".printf(_("Own fingerprint"), res)); btn.visible = true; } } public override void deactivate() { } } }dino-0.4.3/plugins/omemo/src/ui/bad_messages_populator.vala0000644000000000000000000001733214452563620022552 0ustar rootrootusing Gee; using Gtk; using Qlite; using Dino.Entities; using Xmpp; namespace Dino.Plugins.Omemo { public enum BadnessType { UNTRUSTED, UNDECRYPTABLE } public class BadMessagesPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "bad_omemo_messages"; } } private StreamInteractor stream_interactor; private Plugin plugin; private Database db; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private Gee.List bad_items = new ArrayList(); public BadMessagesPopulator(StreamInteractor stream_interactor, Plugin plugin) { this.stream_interactor = stream_interactor; this.plugin = plugin; this.db = plugin.db; plugin.trust_manager.bad_message_state_updated.connect((account, jid, device_id) => { clear_state(); init_state(); }); } private void init_state() { if (current_conversation == null) return; if (current_conversation.type_ == Conversation.Type.GROUPCHAT_PM) return; var qry = db.identity_meta.select() .join_with(db.identity, db.identity.id, db.identity_meta.identity_id) .with(db.identity.account_id, "=", current_conversation.account.id) .where("last_message_untrusted is not NULL OR last_message_undecryptable is not NULL"); switch (current_conversation.type_) { case Conversation.Type.CHAT: qry.with(db.identity_meta.address_name, "=", current_conversation.counterpart.to_string()); break; case Conversation.Type.GROUPCHAT: bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(current_conversation.account, current_conversation.counterpart); if (!is_private) return; var list = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(current_conversation.counterpart, current_conversation.account); if (list == null || list.is_empty) return; var selection = new StringBuilder(); string[] selection_args = {}; foreach (Jid jid in list) { if (selection.len == 0) { selection.append(@" ($(db.identity_meta.address_name) = ?"); } else { selection.append(@" OR $(db.identity_meta.address_name) = ?"); } selection_args += jid.to_string(); } selection.append(")"); qry.where(selection.str, selection_args); break; case Conversation.Type.GROUPCHAT_PM: break; } foreach (Row row in qry) { Jid jid = new Jid(row[db.identity_meta.address_name]); if (!db.identity_meta.last_message_untrusted.is_null(row)) { DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_untrusted]); var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNTRUSTED); bad_items.add(item); item_collection.insert_item(item); } if (!db.identity_meta.last_message_undecryptable.is_null(row)) { DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_undecryptable]); var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNDECRYPTABLE); bad_items.add(item); item_collection.insert_item(item); } } } private void clear_state() { foreach (BadMessageItem bad_item in bad_items) { item_collection.remove_item(bad_item); } bad_items.clear(); } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; init_state(); } public void close(Conversation conversation) { clear_state(); } public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } } public class BadMessageItem : Plugins.MetaConversationItem { private Plugin plugin; private Conversation conversation; private Jid problem_jid; private BadnessType badness_type; public BadMessageItem(Plugin plugin, Conversation conversation, Jid jid, DateTime date, BadnessType badness_type) { this.plugin = plugin; this.conversation = conversation; this.problem_jid = jid; this.time = date; this.badness_type = badness_type; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new BadMessagesWidget(plugin, conversation, problem_jid, badness_type); } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } public class BadMessagesWidget : Box { private Plugin plugin; private Conversation conversation; private Jid jid; private Label label; public BadMessagesWidget(Plugin plugin, Conversation conversation, Jid jid, BadnessType badness_type) { Object(orientation:Orientation.HORIZONTAL, spacing:5); this.plugin = plugin; this.conversation = conversation; this.jid = jid; this.halign = Align.CENTER; this.visible = true; string who = ""; if (conversation.type_ == Conversation.Type.CHAT) { who = Dino.get_participant_display_name(plugin.app.stream_interactor, conversation, jid); } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { who = jid.to_string(); // `jid` is a real JID. In MUCs, try to show nicks instead (given that the JID is currently online) var occupants = plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants == null) return; foreach (Jid occupant in occupants) { if (jid.equals_bare(plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, conversation.account))) { who = occupant.resourcepart; } } } string warning_text = ""; if (badness_type == BadnessType.UNTRUSTED) { warning_text = _("%s has been using an untrusted device. You won't see messages from devices that you do not trust.").printf(who) + " %s".printf(_("Manage devices")); } else { warning_text += _("%s does not trust this device. That means, you might be missing messages.").printf(who); } label = new Label(warning_text) { margin_start=70, margin_end=70, justify=Justification.CENTER, use_markup=true, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true }; label.add_css_class("dim-label"); this.append(label); if (badness_type == BadnessType.UNTRUSTED) { label.activate_link.connect(on_label_activate_link); } } private bool on_label_activate_link() { ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, jid); dialog.set_transient_for((Window) get_root()); dialog.present(); return false; } public override void dispose() { if (label != null) { label.unparent(); label.dispose(); label = null; } base.dispose(); } } } dino-0.4.3/plugins/omemo/src/ui/call_encryption_entry.vala0000644000000000000000000000345714452563620022441 0ustar rootrootusing Dino.Entities; using Gtk; using Qlite; using Xmpp; namespace Dino.Plugins.Omemo { public class CallEncryptionEntry : Plugins.CallEncryptionEntry, Object { private Database db; public CallEncryptionEntry(Database db) { this.db = db; } public Plugins.CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption) { DtlsSrtpVerificationDraft.OmemoContentEncryption? omemo_encryption = encryption as DtlsSrtpVerificationDraft.OmemoContentEncryption; if (omemo_encryption == null) return null; int identity_id = db.identity.get_id(account.id); Row? device = db.identity_meta.get_device(identity_id, omemo_encryption.jid.to_string(), omemo_encryption.sid); if (device == null) return null; TrustLevel trust = (TrustLevel) device[db.identity_meta.trust_level]; return new CallEncryptionWidget(trust); } } public class CallEncryptionWidget : Plugins.CallEncryptionWidget, Object { string? title = null; string? icon = null; bool should_show_keys = false; public CallEncryptionWidget(TrustLevel trust) { if (trust == TrustLevel.VERIFIED) { title = "This call is encrypted and verified with OMEMO."; icon = "dino-security-high-symbolic"; should_show_keys = false; } else { title = "This call is encrypted with OMEMO."; should_show_keys = true; } } public string? get_title() { return title; } public string? get_icon_name() { return icon; } public bool show_keys() { return should_show_keys; } } } dino-0.4.3/plugins/omemo/src/ui/contact_details_dialog.vala0000644000000000000000000004117114452563620022505 0ustar rootrootusing Gtk; using Xmpp; using Gee; using Qlite; using Dino.Entities; using Qrencode; using Gdk; namespace Dino.Plugins.Omemo { [GtkTemplate (ui = "/im/dino/Dino/omemo/contact_details_dialog.ui")] public class ContactDetailsDialog : Gtk.Dialog { private Plugin plugin; private Account account; private Jid jid; private bool own = false; private int own_id = 0; private int identity_id = 0; private Signal.Store store; private Set displayed_ids = new HashSet(); [GtkChild] private unowned Label automatically_accept_new_label; [GtkChild] private unowned Label automatically_accept_new_descr; [GtkChild] private unowned Label own_key_label; [GtkChild] private unowned Label new_keys_label; [GtkChild] private unowned Label associated_keys_label; [GtkChild] private unowned Label inactive_expander_label; [GtkChild] private unowned Box own_fingerprint_container; [GtkChild] private unowned Label own_fingerprint_label; [GtkChild] private unowned Box new_keys_container; [GtkChild] private unowned ListBox new_keys_listbox; [GtkChild] private unowned Box keys_container; [GtkChild] private unowned ListBox keys_listbox; [GtkChild] private unowned Expander inactive_keys_expander; [GtkChild] private unowned ListBox inactive_keys_listbox; [GtkChild] private unowned Switch auto_accept_switch; [GtkChild] private unowned Button copy_button; [GtkChild] private unowned MenuButton show_qrcode_button; [GtkChild] private unowned Picture qrcode_picture; [GtkChild] private unowned Popover qrcode_popover; private ArrayList new_keys_listbox_children = new ArrayList(); construct { // If we set the strings in the .ui file, they don't get translated title = _("OMEMO Key Management"); automatically_accept_new_label.label = _("Automatically accept new keys"); automatically_accept_new_descr.label = _("New encryption keys from this contact will be accepted automatically."); own_key_label.label = _("Own key"); new_keys_label.label = _("New keys"); associated_keys_label.label = _("Associated keys"); inactive_expander_label.label = _("Inactive keys"); } public ContactDetailsDialog(Plugin plugin, Account account, Jid jid) { Object(use_header_bar : Environment.get_variable("GTK_CSD") != "0" ? 1 : 0); this.plugin = plugin; this.account = account; this.jid = jid; if (Environment.get_variable("GTK_CSD") != "0") { // ((HeaderBar) get_header_bar()).set_subtitle(jid.bare_jid.to_string()); } keys_listbox.row_activated.connect(on_key_entry_clicked); inactive_keys_listbox.row_activated.connect(on_key_entry_clicked); auto_accept_switch.state_set.connect(on_auto_accept_toggled); identity_id = plugin.db.identity.get_id(account.id); if (identity_id < 0) return; Dino.Application? app = Application.get_default() as Dino.Application; if (app != null) { store = app.stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).store; } auto_accept_switch.set_active(plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true)); // Dialog opened from the account settings menu // Show the fingerprint for this device separately with buttons for a qrcode and to copy if(jid.equals(account.bare_jid)) { own = true; own_id = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; automatically_accept_new_descr.label = _("New encryption keys from your other devices will be accepted automatically."); own_fingerprint_container.visible = true; string own_b64 = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.identity_key_public_base64]; string fingerprint = fingerprint_from_base64(own_b64); own_fingerprint_label.set_markup(fingerprint_markup(fingerprint)); copy_button.clicked.connect(() => { copy_button.get_clipboard().set_text(fingerprint); }); int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; #if GLIB_2_66 && VALA_0_50 string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null); #else var iri_path_seg = escape_for_iri_path_segment(jid.to_string()); var iri = @"xmpp:$(iri_path_seg)?$(iri_query)"; #endif const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 const int MODULE_SIZE_PX = 4; // arbitrary var qr_paintable = new QRcode(iri, 2) .to_paintable(MODULE_SIZE_PX * qrcode_picture.scale_factor); qrcode_picture.paintable = qr_paintable; qrcode_picture.margin_top = qrcode_picture.margin_end = qrcode_picture.margin_bottom = qrcode_picture.margin_start = QUIET_ZONE_MODULES * MODULE_SIZE_PX; qrcode_popover.add_css_class("qrcode-container"); show_qrcode_button.popover = qrcode_popover; } new_keys_listbox.set_header_func(header_function); keys_listbox.set_header_func(header_function); //Show any new devices for which the user must decide whether to accept or reject foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { add_new_fingerprint(device); } //Show the normal devicelist foreach (Row device in plugin.db.identity_meta.get_known_devices(identity_id, jid.to_string())) { if(own && device[plugin.db.identity_meta.device_id] == own_id) { continue; } add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]); } // Check for unknown devices fetch_unknown_bundles(); } private static string escape_for_iri_path_segment(string s) { // from RFC 3986, 2.2. Reserved Characters: string SUB_DELIMS = "!$&'()*+,;="; // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded): string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true); } private void fetch_unknown_bundles() { Dino.Application app = Application.get_default() as Dino.Application; XmppStream? stream = app.stream_interactor.get_stream(account); if (stream == null) return; StreamModule? module = stream.get_module(StreamModule.IDENTITY); if (module == null) return; module.bundle_fetched.connect_after((bundle_jid, device_id, bundle) => { if (bundle_jid.equals(jid) && !displayed_ids.contains(device_id)) { Row? device = plugin.db.identity_meta.get_device(identity_id, jid.to_string(), device_id); if (device == null) return; if (auto_accept_switch.active) { add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]); } else { add_new_fingerprint(device); } } }); foreach (Row device in plugin.db.identity_meta.get_unknown_devices(identity_id, jid.to_string())) { try { module.fetch_bundle(stream, new Jid(device[plugin.db.identity_meta.address_name]), device[plugin.db.identity_meta.device_id], false); } catch (InvalidJidError e) { warning("Ignoring device with invalid Jid: %s", e.message); } } } private void header_function(ListBoxRow row, ListBoxRow? before) { if (row.get_header() == null && before != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } private void add_fingerprint(Row device, TrustLevel trust) { string key_base64 = device[plugin.db.identity_meta.identity_key_public_base64]; bool key_active = device[plugin.db.identity_meta.now_active]; if (store != null) { try { Signal.Address address = new Signal.Address(jid.to_string(), device[plugin.db.identity_meta.device_id]); Signal.SessionRecord? session = null; if (store.contains_session(address)) { session = store.load_session(address); string session_key_base64 = Base64.encode(session.state.remote_identity_key.serialize()); if (key_base64 != session_key_base64) { critical("Session and database identity key mismatch!"); key_base64 = session_key_base64; } } } catch (Error e) { print("Error while reading session store: %s", e.message); } } FingerprintRow fingerprint_row = new FingerprintRow(device, key_base64, trust, key_active) { visible = true, activatable = true, hexpand = true }; if (device[plugin.db.identity_meta.now_active]) { keys_container.visible = true; keys_listbox.append(fingerprint_row); } else { inactive_keys_expander.visible=true; inactive_keys_listbox.append(fingerprint_row); } displayed_ids.add(device[plugin.db.identity_meta.device_id]); } private void on_key_entry_clicked(ListBoxRow widget) { FingerprintRow? fingerprint_row = widget as FingerprintRow; if (fingerprint_row == null) return; Row updated_device = plugin.db.identity_meta.get_device(fingerprint_row.row[plugin.db.identity_meta.identity_id], fingerprint_row.row[plugin.db.identity_meta.address_name], fingerprint_row.row[plugin.db.identity_meta.device_id]); ManageKeyDialog manage_dialog = new ManageKeyDialog(updated_device, plugin.db); manage_dialog.set_transient_for((Gtk.Window) get_root()); manage_dialog.present(); manage_dialog.response.connect((response) => { fingerprint_row.update_trust_state(response, fingerprint_row.row[plugin.db.identity_meta.now_active]); update_stored_trust(response, fingerprint_row.row); }); } private bool on_auto_accept_toggled(bool active) { plugin.trust_manager.set_blind_trust(account, jid, active); if (active) { int identity_id = plugin.db.identity.get_id(account.id); if (identity_id < 0) return false; new_keys_container.visible = false; foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); add_fingerprint(device, TrustLevel.TRUSTED); } } return false; } private void update_stored_trust(int response, Row device) { switch (response) { case TrustLevel.TRUSTED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); break; case TrustLevel.UNTRUSTED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); break; case TrustLevel.VERIFIED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.VERIFIED); plugin.trust_manager.set_blind_trust(account, jid, false); auto_accept_switch.set_active(false); break; } } private void add_new_fingerprint(Row device) { new_keys_container.visible = true; ListBoxRow lbr = new ListBoxRow() { visible = true, activatable = false, hexpand = true }; Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true }; Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; accept_button.set_icon_name("emblem-ok-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita accept_button.add_css_class("suggested-action"); accept_button.tooltip_text = _("Accept key"); Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; reject_button.set_icon_name("action-unavailable-symbolic"); reject_button.add_css_class("destructive-action"); reject_button.tooltip_text = _("Reject key"); accept_button.clicked.connect(() => { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); add_fingerprint(device, TrustLevel.TRUSTED); new_keys_listbox.remove(lbr); new_keys_listbox_children.remove(lbr); if (new_keys_listbox_children.size < 1) new_keys_container.visible = false; }); reject_button.clicked.connect(() => { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); add_fingerprint(device, TrustLevel.UNTRUSTED); new_keys_listbox.remove(lbr); new_keys_listbox_children.remove(lbr); if (new_keys_listbox_children.size < 1) new_keys_container.visible = false; }); string res = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64])); Label fingerprint_label = new Label(res) { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false }; box.append(fingerprint_label); Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true }; control_box.append(accept_button); control_box.append(reject_button); control_box.add_css_class("linked"); // .linked: Visually link the accept / reject buttons box.append(control_box); lbr.set_child(box); new_keys_listbox.append(lbr); new_keys_listbox_children.add(lbr); displayed_ids.add(device[plugin.db.identity_meta.device_id]); } } public class FingerprintRow : ListBoxRow { private Image trust_image = new Image() { visible = true, halign = Align.END }; private Label fingerprint_label = new Label("") { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false }; private Label trust_label = new Label(null) { visible = true, hexpand = true, xalign = 0 }; public Row row; construct { Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true }; Box status_box = new Box(Gtk.Orientation.HORIZONTAL, 5) { visible = true, hexpand = true }; box.append(fingerprint_label); box.append(status_box); status_box.append(trust_label); status_box.append(trust_image); this.set_child(box); } public FingerprintRow(Row row, string key_base64, int trust, bool now_active) { this.row = row; fingerprint_label.label = fingerprint_markup(fingerprint_from_base64(key_base64)); update_trust_state(trust, now_active); } public void update_trust_state(int trust, bool now_active) { switch(trust) { case TrustLevel.TRUSTED: trust_image.icon_name = "emblem-ok-symbolic"; trust_label.set_markup("%s".printf(_("Accepted"))); fingerprint_label.remove_css_class("dim-label"); break; case TrustLevel.UNTRUSTED: trust_image.icon_name = "action-unavailable-symbolic"; trust_label.set_markup("%s".printf(_("Rejected"))); fingerprint_label.add_css_class("dim-label"); break; case TrustLevel.VERIFIED: trust_image.icon_name = "security-high-symbolic"; trust_label.set_markup("%s".printf(_("Verified"))); fingerprint_label.remove_css_class("dim-label"); break; } if (!now_active) { trust_image.icon_name = "appointment-missed-symbolic"; trust_label.set_markup("%s".printf(_("Unused"))); } } } } dino-0.4.3/plugins/omemo/src/ui/contact_details_provider.vala0000644000000000000000000000336614452563620023104 0ustar rootrootusing Gtk; using Gee; using Qlite; using Dino.Entities; namespace Dino.Plugins.Omemo { public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "omemo_info"; } } private Plugin plugin; public ContactDetailsProvider(Plugin plugin) { this.plugin = plugin; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) { int identity_id = plugin.db.identity.get_id(conversation.account.id); if (identity_id < 0) return; int i = 0; foreach (Row row in plugin.db.identity_meta.with_address(identity_id, conversation.counterpart.to_string())) { if (row[plugin.db.identity_meta.identity_key_public_base64] != null) { i++; } } if (i > 0) { Button btn = new Button.from_icon_name("view-list-symbolic") { visible = true, valign = Align.CENTER, has_frame = false }; btn.clicked.connect(() => { btn.activate(); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, conversation.counterpart); dialog.set_transient_for((Window) btn.get_root()); dialog.response.connect((response_type) => { plugin.device_notification_populator.should_hide(); }); dialog.present(); }); contact_details.add(_("Encryption"), "OMEMO", n("%d OMEMO device", "%d OMEMO devices", i).printf(i), btn); } } } } } dino-0.4.3/plugins/omemo/src/ui/device_notification_populator.vala0000644000000000000000000000656114452563620024144 0ustar rootrootusing Dino.Entities; using Xmpp; using Gtk; namespace Dino.Plugins.Omemo { public class DeviceNotificationPopulator : NotificationPopulator, Object { public string id { get { return "device_notification"; } } private StreamInteractor? stream_interactor; private Plugin plugin; private Conversation? current_conversation; private NotificationCollection? notification_collection; private ConversationNotification notification; public DeviceNotificationPopulator(Plugin plugin, StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.plugin = plugin; stream_interactor.account_added.connect(on_account_added); } public void init(Conversation conversation, NotificationCollection notification_collection, Plugins.WidgetType type) { current_conversation = conversation; this.notification_collection = notification_collection; if (plugin.has_new_devices(conversation.account, conversation.counterpart) && conversation.type_ == Conversation.Type.CHAT) { display_notification(); } } public void close(Conversation conversation) { notification = null; } private void display_notification() { if (notification == null) { notification = new ConversationNotification(plugin, current_conversation.account, current_conversation.counterpart); notification.should_hide.connect(should_hide); notification_collection.add_meta_notification(notification); } } public void should_hide() { if (!plugin.has_new_devices(current_conversation.account, current_conversation.counterpart) && notification != null){ notification_collection.remove_meta_notification(notification); notification = null; } } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { if (current_conversation != null && jid.equals(current_conversation.counterpart) && plugin.has_new_devices(current_conversation.account, current_conversation.counterpart)) { display_notification(); } }); } } private class ConversationNotification : MetaConversationNotification { private Widget widget; private Plugin plugin; private Jid jid; private Account account; public signal void should_hide(); public ConversationNotification(Plugin plugin, Account account, Jid jid) { this.plugin = plugin; this.jid = jid; this.account = account; Box box = new Box(Orientation.HORIZONTAL, 5); Button manage_button = new Button.with_label(_("Manage")); manage_button.clicked.connect(() => { manage_button.activate(); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, jid); dialog.set_transient_for((Window) manage_button.get_root()); dialog.response.connect((response_type) => { should_hide(); }); dialog.present(); }); box.append(new Label(_("This contact has new devices")) { margin_end=10 }); box.append(manage_button); widget = box; } public override Object? get_widget(WidgetType type) { return widget; } } } dino-0.4.3/plugins/omemo/src/ui/encryption_list_entry.vala0000644000000000000000000000624514452563620022477 0ustar rootrootusing Dino.Entities; using Gtk; using Qlite; using Xmpp; namespace Dino.Plugins.Omemo { public class EncryptionListEntry : Plugins.EncryptionListEntry, Object { private Plugin plugin; private Database db; public EncryptionListEntry(Plugin plugin) { this.plugin = plugin; this.db = plugin.db; } public Entities.Encryption encryption { get { return Entities.Encryption.OMEMO; }} public string name { get { return "OMEMO"; }} public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { return null; } public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { if (content_item.encryption != encryption) return null; RowOption row = db.content_item_meta.select( { db.identity_meta.trust_level } ).with(db.content_item_meta.content_item_id, "=", content_item.id) .join_on(db.identity_meta, @"$(db.identity_meta.address_name)=$(db.content_item_meta.address_name) AND $(db.identity_meta.device_id)=$(db.content_item_meta.device_id)") .single().row(); if (row.is_present() && (TrustLevel) row[db.identity_meta.trust_level] == TrustLevel.VERIFIED) { return "dino-security-high-symbolic"; } return null; } public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { encryption_activated_async.begin(conversation, input_status_callback); } public async void encryption_activated_async(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { input_status_callback(new Plugins.InputFieldStatus("Can't use encryption in a groupchat private message.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } MucManager muc_manager = plugin.app.stream_interactor.get_module(MucManager.IDENTITY); Manager omemo_manager = plugin.app.stream_interactor.get_module(Manager.IDENTITY); if (muc_manager.is_private_room(conversation.account, conversation.counterpart)) { foreach (Jid offline_member in muc_manager.get_offline_members(conversation.counterpart, conversation.account)) { bool ok = yield omemo_manager.ensure_get_keys_for_jid(conversation.account, offline_member); if (!ok) { input_status_callback(new Plugins.InputFieldStatus("A member does not support OMEMO: %s".printf(offline_member.to_string()), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } } return; } if (!(yield omemo_manager.ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid))) { input_status_callback(new Plugins.InputFieldStatus("This contact does not support %s encryption".printf("OMEMO"), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); } } } } dino-0.4.3/plugins/omemo/src/ui/manage_key_dialog.vala0000644000000000000000000002070014452563620021440 0ustar rootrootusing Gtk; using Qlite; namespace Dino.Plugins.Omemo { [GtkTemplate (ui = "/im/dino/Dino/omemo/manage_key_dialog.ui")] public class ManageKeyDialog : Gtk.Dialog { [GtkChild] private unowned Stack manage_stack; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Label main_desc_label; [GtkChild] private unowned ListBox main_action_list; [GtkChild] private unowned Image confirm_image; [GtkChild] private unowned Label confirm_title_label; [GtkChild] private unowned Label confirm_desc_label; [GtkChild] private unowned Label verify_label; [GtkChild] private unowned Label compare_fingerprint_label; [GtkChild] private unowned Button verify_yes_button; [GtkChild] private unowned Button verify_no_button; private Row device; private Database db; private bool return_to_main; private int current_response; construct { // If we set the strings in the .ui file, they don't get translated this.title = _("Manage Key"); compare_fingerprint_label.label = _("Compare the fingerprint, character by character, with the one shown on your contact's device."); verify_no_button.label = _("Fingerprints differ"); verify_yes_button.label = _("Fingerprints match"); cancel_button.label = _("Cancel"); ok_button.label = _("Confirm"); } public ManageKeyDialog(Row device, Database db) { Object(use_header_bar : Environment.get_variable("GTK_CSD") != "0" ? 1 : 0); this.device = device; this.db = db; setup_main_screen(); setup_verify_screen(); cancel_button.clicked.connect(handle_cancel); ok_button.clicked.connect(() => { response(current_response); close(); }); verify_yes_button.clicked.connect(() => { confirm_image.set_from_icon_name("security-high-symbolic"); confirm_title_label.label = _("Verify key"); confirm_desc_label.set_markup(_("Future messages sent by %s from the device that uses this key will be highlighted accordingly in the chat window.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = false; current_response = TrustLevel.VERIFIED; }); verify_no_button.clicked.connect(() => { return_to_main = false; confirm_image.set_from_icon_name("dialog-warning-symbolic"); confirm_title_label.label = _("Fingerprints do not match"); confirm_desc_label.set_markup(_("Please verify that you are comparing the correct fingerprint. If fingerprints do not match, %s's account may be compromised and you should consider rejecting this key.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); }); } private void handle_cancel() { if (manage_stack.get_visible_child_name() == "main") close(); if (manage_stack.get_visible_child_name() == "verify") { manage_stack.set_visible_child_name("main"); cancel_button.label = _("Cancel"); } if (manage_stack.get_visible_child_name() == "confirm") { if (return_to_main) { manage_stack.set_visible_child_name("main"); cancel_button.label = _("Cancel"); } else { manage_stack.set_visible_child_name("verify"); } } ok_button.sensitive = false; } private Box make_action_box(string title, string desc){ Box box = new Box(Orientation.VERTICAL, 0) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14 }; Label lbl_title = new Label(title) { visible = true, halign = Align.START }; Label lbl_desc = new Label(desc) { visible = true, xalign = 0, wrap = true, max_width_chars = 40 }; Pango.AttrList title_attrs = new Pango.AttrList(); title_attrs.insert(Pango.attr_scale_new(1.1)); lbl_title.attributes = title_attrs; Pango.AttrList desc_attrs = new Pango.AttrList(); desc_attrs.insert(Pango.attr_scale_new(0.8)); lbl_desc.attributes = desc_attrs; lbl_desc.add_css_class("dim-label"); box.append(lbl_title); box.append(lbl_desc); return box; } private void setup_main_screen() { main_action_list.set_header_func((row, before_row) => { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } }); ListBoxRow verify_row = new ListBoxRow() { visible = true }; verify_row.set_child(make_action_box(_("Verify key fingerprint"), _("Compare this key's fingerprint with the fingerprint displayed on the contact's device."))); ListBoxRow reject_row = new ListBoxRow() { visible = true }; reject_row.set_child(make_action_box(_("Reject key"), _("Block encrypted communication with the contact's device that uses this key."))); ListBoxRow accept_row = new ListBoxRow() {visible = true }; accept_row.set_child(make_action_box(_("Accept key"), _("Allow encrypted communication with the contact's device that uses this key."))); switch((TrustLevel) device[db.identity_meta.trust_level]) { case TrustLevel.TRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("accepted")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.append(verify_row); main_action_list.append(reject_row); break; case TrustLevel.VERIFIED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("verified")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])") + " " + _("Additionally it has been verified to match the key on the contact's device.")); main_action_list.append(reject_row); break; case TrustLevel.UNTRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("rejected")+"")+" "+_("This means it cannot be used by %s to decipher your messages, and you won't see messages encrypted with it.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.append(accept_row); break; } //Row clicked - go to appropriate screen main_action_list.row_activated.connect((row) => { if(row == verify_row) { manage_stack.set_visible_child_name("verify"); } else if (row == reject_row) { confirm_image.set_from_icon_name("action-unavailable-symbolic"); confirm_title_label.label = _("Reject key"); confirm_desc_label.set_markup(_("You won't see encrypted messages from the device of %s that uses this key. Conversely, that device won't be able to decipher your messages anymore.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; current_response = TrustLevel.UNTRUSTED; } else if (row == accept_row) { confirm_image.set_from_icon_name("emblem-ok-symbolic"); confirm_title_label.label = _("Accept key"); confirm_desc_label.set_markup(_("You will be able to exchange encrypted messages with the device of %s that uses this key.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; current_response = TrustLevel.TRUSTED; } cancel_button.label = _("Back"); }); manage_stack.set_visible_child_name("main"); } private void setup_verify_screen() { verify_label.set_markup(fingerprint_markup(fingerprint_from_base64(device[db.identity_meta.identity_key_public_base64]))); } } } dino-0.4.3/plugins/omemo/src/ui/own_notifications.vala0000644000000000000000000000244614452563620021564 0ustar rootrootusing Dino.Entities; using Xmpp; using Gtk; namespace Dino.Plugins.Omemo { public class OwnNotifications { private StreamInteractor stream_interactor; private Plugin plugin; private Account account; public OwnNotifications (Plugin plugin, StreamInteractor stream_interactor, Account account) { this.stream_interactor = (!)stream_interactor; this.plugin = plugin; this.account = account; stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { if (jid.equals(account.bare_jid) && plugin.has_new_devices(account, account.bare_jid)) { display_notification(); } }); if (plugin.has_new_devices(account, account.bare_jid)) { display_notification(); } } private void display_notification() { Notification notification = new Notification(_("OMEMO trust decision required")); notification.set_default_action_and_target_value("app.own-keys", new Variant.int32(account.id)); notification.set_body(_("Did you add a new device for account %s?").printf(@"$(account.bare_jid.to_string())")); plugin.app.send_notification(account.id.to_string()+"-new-device", notification); } } } dino-0.4.3/plugins/omemo/src/ui/util.vala0000644000000000000000000000346214452563620017004 0ustar rootrootusing Xmpp.Util; namespace Dino.Plugins.Omemo { public static string fingerprint_from_base64(string b64) { uint8[] arr = Base64.decode(b64); arr = arr[1:arr.length]; string s = ""; foreach (uint8 i in arr) { string tmp = i.to_string("%x"); if (tmp.length == 1) tmp = "0" + tmp; s = s + tmp; } return s; } public static string fingerprint_markup(string s) { string markup = ""; for (int i = 0; i < s.length; i += 4) { string four_chars = s.substring(i, 4).down(); int raw = (int) from_hex(four_chars); uint8[] bytes = {(uint8) ((raw >> 8) & 0xff - 128), (uint8) (raw & 0xff - 128)}; Checksum checksum = new Checksum(ChecksumType.SHA1); checksum.update(bytes, bytes.length); uint8[] digest = new uint8[20]; size_t len = 20; checksum.get_digest(digest, ref len); uint8 r = digest[0]; uint8 g = digest[1]; uint8 b = digest[2]; if (r == 0 && g == 0 && b == 0) r = g = b = 1; double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b; if (brightness < 80) { double factor = 80.0 / brightness; r = uint8.min(255, (uint8) (r * factor)); g = uint8.min(255, (uint8) (g * factor)); b = uint8.min(255, (uint8) (b * factor)); } else if (brightness > 180) { double factor = 180.0 / brightness; r = (uint8) (r * factor); g = (uint8) (g * factor); b = (uint8) (b * factor); } if (i % 32 == 0 && i != 0) markup += "\n"; markup += @"$four_chars"; if (i % 8 == 4 && i % 32 != 28) markup += " "; } return "" + markup + ""; } } dino-0.4.3/plugins/omemo/vapi/0000755000000000000000000000000014452563620014710 5ustar rootrootdino-0.4.3/plugins/omemo/vapi/libqrencode.vapi0000644000000000000000000000577414452563620020075 0ustar rootroot[CCode (cheader_filename = "qrencode.h")] namespace Qrencode { [CCode (cname = "QRecLevel", cprefix = "QR_ECLEVEL_")] public enum ECLevel { L, M, Q, H } [CCode (cname = "QRencodeMode", cprefix = "QR_MODE_")] public enum EncodeMode { NUL, NUM, AN, [CCode (cname = "QR_MODE_8")] EIGHT_BIT, KANJI, STRUCTURE, ECI, FNC1FIRST, FNC1SECOND } [CCode (cname = "QRcode", free_function = "QRcode_free", has_type_id = false)] [Compact] public class QRcode { private int version; private int width; [CCode (array_length = false)] private uint8[] data; [CCode (cname = "QRcode_encodeString")] public QRcode (string str, int version = 0, ECLevel level = ECLevel.L, EncodeMode hint = EncodeMode.EIGHT_BIT, bool casesensitive = true); public Gdk.Paintable to_paintable(int module_size) { GLib.assert(module_size > 0); var dst_width = width*module_size; var dst_data = new uint8[dst_width*dst_width*3]; expand_and_upsample(data,width,width, dst_data,dst_width,dst_width); return new Gdk.MemoryTexture(dst_width, dst_width, Gdk.MemoryFormat.R8G8B8, new GLib.Bytes.take((owned) dst_data), dst_width*3); } /** Does 2D nearest-neighbor upsampling of an array of single-byte * samples, while expanding the least significant bit of each sample * to three 0-or-255 bytes. */ private void expand_and_upsample( uint8[] src, uint src_w, uint src_h, uint8[] dst, uint dst_w, uint dst_h) { GLib.assert(dst_w % src_w == 0); GLib.assert(dst_h % src_h == 0); var scale_x = dst_w/src_w, scale_y = dst_h/src_h; /* Doing the iteration in the order of destination samples for * improved cache-friendliness (dst is 48 times larger than src in * the typical case of scaling by 4x4). * The choice of multiple nested loops over a single one is for * avoiding a ton of divisions by non-constants. */ for (uint src_y = 0; src_y < src_h; ++src_y) { for (uint repeat_y = 0; repeat_y < scale_y; ++repeat_y) { var dst_y = src_y*scale_y + repeat_y; for (uint src_x = 0; src_x < src_w; ++src_x) { uint8 value = (src[src_y*src_w + src_x] & 1)==1 ? 0:255; for (uint repeat_x = 0; repeat_x < scale_x; ++repeat_x){ var dst_x = src_x*scale_x + repeat_x; var dst_idx = dst_y*dst_w + dst_x; dst[dst_idx*3+0] = value; dst[dst_idx*3+1] = value; dst[dst_idx*3+2] = value; } } } } } } } dino-0.4.3/plugins/openpgp/0000755000000000000000000000000014452563620014305 5ustar rootrootdino-0.4.3/plugins/openpgp/CMakeLists.txt0000644000000000000000000000344014452563620017046 0ustar rootrootset(GETTEXT_PACKAGE "dino-openpgp") find_package(Gettext) include(${GETTEXT_USE_FILE}) gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/po TARGET_NAME ${GETTEXT_PACKAGE}-translations) find_packages(OPENPGP_PACKAGES REQUIRED Gee GLib GModule GObject GTK4 ) set(RESOURCE_LIST account_settings_item.ui ) compile_gresources( OPENPGP_GRESOURCES_TARGET OPENPGP_GRESOURCES_XML TARGET ${CMAKE_CURRENT_BINARY_DIR}/resources/resources.c TYPE EMBED_C RESOURCES ${RESOURCE_LIST} PREFIX /im/dino/Dino/openpgp SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data ) vala_precompile(OPENPGP_VALA_C SOURCES src/file_transfer/file_decryptor.vala src/file_transfer/file_encryptor.vala src/account_settings_entry.vala src/contact_details_provider.vala src/database.vala src/encryption_list_entry.vala src/manager.vala src/plugin.vala src/register_plugin.vala src/stream_flag.vala src/stream_module.vala src/util.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/gpgme.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi PACKAGES ${OPENPGP_PACKAGES} GRESOURCES ${OPENPGP_GRESOURCES_XML} ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="OpenPGP" -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\") add_library(openpgp SHARED ${OPENPGP_VALA_C} ${OPENPGP_GRESOURCES_TARGET}) add_dependencies(openpgp ${GETTEXT_PACKAGE}-translations) target_link_libraries(openpgp libdino gpgme-vala ${OPENPGP_PACKAGES}) set_target_properties(openpgp PROPERTIES PREFIX "") set_target_properties(openpgp PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS openpgp ${PLUGIN_INSTALL}) dino-0.4.3/plugins/openpgp/data/0000755000000000000000000000000014452563620015216 5ustar rootrootdino-0.4.3/plugins/openpgp/data/account_settings_item.ui0000644000000000000000000000222014452563620022143 0ustar rootroot label 0 0 0 entry 1 dino-0.4.3/plugins/openpgp/po/0000755000000000000000000000000014452563620014723 5ustar rootrootdino-0.4.3/plugins/openpgp/po/LINGUAS0000644000000000000000000000016514452563620015752 0ustar rootrootar ca cs de el en eo es eu fa fr gl hu id ie is it ja ko lb lt lv nb nl oc pl pt pt_BR ro ru sq sv tr uk zh_CN zh_TW dino-0.4.3/plugins/openpgp/po/ar.po0000644000000000000000000000342414452563620015670 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-10-23 17:22+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 3.3-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "نشر المفاتيح معطل" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "خطأ في GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "لا مفتاح متوفر. قم بتوليد واحد !" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "اختيار مفتاح" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "جارٍ التحميل…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "استعلام GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "المفتاح ليس في سلسلة المفاتيح" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "التشفير" dino-0.4.3/plugins/openpgp/po/ca.po0000644000000000000000000000300714452563620015646 0ustar rootroot# Catalan translation for dino-openpgp. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018. # msgid "" msgstr "" "Project-Id-Version: dino-openpgp 20180123\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-01-24 11:24+0100\n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publicació de claus inhabilitada" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "S'ha produït un error al GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "No hi ha claus disponibles. Genereu una!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Seleccioneu una clau" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "S'està carregant…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "S'està consultant al GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "La clau no està al clauer" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Xifratge" dino-0.4.3/plugins/openpgp/po/cs.po0000644000000000000000000000315314452563620015672 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-08-21 05:36+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publikování klíčů zakázáno" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Chyba v GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nejsou k dispozici žádné klíče. Vygenerujte jeden!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Vybrat klíč" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Načítání…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Dotazování GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Klíč není v klíčence" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Šifrování" dino-0.4.3/plugins/openpgp/po/de.po0000644000000000000000000000300014452563620015644 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-04-16 20:11+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Schlüsselveröffentlichung deaktiviert" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Fehler in GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Keine Schlüssel vorhanden. Erzeuge einen!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Wähle einen Schlüssel" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Lade…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Frage GnuPG ab" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Schlüssel nicht im Schlüsselbund" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Verschlüsselung" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.4.3/plugins/openpgp/po/dino-openpgp.pot0000644000000000000000000000261414452563620020051 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "" dino-0.4.3/plugins/openpgp/po/el.po0000644000000000000000000000337614452563620015674 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-02-04 09:55+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Η δημοσίευση του κλειδιού είναι απενεργοποιημένη" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Σφάλμα στο GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Δεν υπάρχουν διαθέσιμα κλειδιά. Δημιουργήστε ένα!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Επιλέξτε κλειδί" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Φόρτωση…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Αναζήτηση στο GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Το κλειδί δεν είναι στο keychain" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Κρυπτογράφηση" dino-0.4.3/plugins/openpgp/po/en.po0000644000000000000000000000210314452563620015661 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "" dino-0.4.3/plugins/openpgp/po/eo.po0000644000000000000000000000310414452563620015664 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-08 22:02+0000\n" "Language-Team: none\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5.1\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Eldonado de ŝlosilo estas malŝaltita" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Eraro pri GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Neniu ŝlosilo estas havebla. Faru ŝlosilon!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Elekti ŝlosilon" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Ŝargante…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Pridemandante GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Ŝlosilo ne en ŝlosilaro" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Ĉifrado" dino-0.4.3/plugins/openpgp/po/es.po0000644000000000000000000000321414452563620015672 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-11-19 08:07+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.3-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publicación de claves desactivada" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Error en GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "No hay claves disponibles. ¡Genera una!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Seleccionar clave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Cargando…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "La clave no está en la cadena de claves" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Cifrado" dino-0.4.3/plugins/openpgp/po/eu.po0000644000000000000000000000317214452563620015677 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-04-03 15:27+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.20-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Gakoa argitaratzea ezgaituta" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG akatsa" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Ez dago gakorik eskuragarri. Sortu ezazu bat!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Gakoa hautatu" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Kargatzen…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "GnuPGri galdetzen" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Gakorik ez giltzatakoan" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Enkriptazioa" dino-0.4.3/plugins/openpgp/po/fa.po0000644000000000000000000000326614452563620015660 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-04-26 15:32+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.7-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "انشار کلید غیرفعال‌شده" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "خطا در گنوپی‌جی" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "هیچ کلیدی در دسترس نیست. یکی تولید کنید!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "انتخاب کلید" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "در حال بارگذاری…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "پرس‌و‌جوی گنوپی‌جی" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "کلید در دسته‌کلید نیست" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "رمزگذاری" dino-0.4.3/plugins/openpgp/po/fr.po0000644000000000000000000000325114452563620015673 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-12 17:21+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "La publication des clés est désactivée" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Erreur dans GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Aucune clé n’est disponible. Générez-en une !" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Choix d’une clé" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Chargement…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Interrogation de GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "La clé n’est pas dans le trousseau" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Chiffrement" dino-0.4.3/plugins/openpgp/po/gl.po0000644000000000000000000000320514452563620015665 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-02-07 10:50+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Desactivada a publicación de chaves" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Fallo en GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Sen chaves dispoñibles. Crear unha!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Escolle chave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Cargando…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Consultando a GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "A chave non está no anel de chaves" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Cifrado" dino-0.4.3/plugins/openpgp/po/hu.po0000644000000000000000000000322714452563620015703 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-12-05 01:51+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.10-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Kulcsközzététel letiltva" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Hiba a GnuPG-ben" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nincsenek elérhető kulcsok. Állítson elő egyet!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Kulcs kiválasztása" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Betöltés…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "A GnuPG lekérése" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "A kulcs nincs a kulcstartóban" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Titkosítás" dino-0.4.3/plugins/openpgp/po/id.po0000644000000000000000000000306614452563620015664 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-01-09 11:43+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Penerbitan kunci dinonaktifkan" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Kesalahan pada GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Kunci tidak tersedia. Buat satu!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Pilih kunci" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Memuat…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Bertanya kepada GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Kunci tidak ada di gantungan kunci" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Enkripsi" dino-0.4.3/plugins/openpgp/po/ie.po0000644000000000000000000000307214452563620015662 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-02-04 16:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publication de claves depermisset" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Errore de GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Null claves disponibil. Crea ún!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Selecter un clave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Cargante…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Contactante GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Clave ne es in li porta-clave" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Ciffration" dino-0.4.3/plugins/openpgp/po/is.po0000644000000000000000000000307514452563620015703 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-10-17 00:56+0000\n" "Language-Team: none\n" "Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n" "X-Generator: Weblate 4.15-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Deiling lykla óvirkt" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Villa hjá GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Engir lyklar í boði. Búðu einn til!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Veldu lykil" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Hleður…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Að spyrja GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Lykill ekki í lyklakippu" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Dulritun" dino-0.4.3/plugins/openpgp/po/it.po0000644000000000000000000000300114452563620015671 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-01-24 12:18+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.19-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Pubblicazione della chiave disabilitata" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Errore in GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nessuna chiave disponibile. Generane una!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Seleziona una chiave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Caricamento…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Interrogando GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "La chiave non è nel portachiavi" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Crittografia" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.4.3/plugins/openpgp/po/ja.po0000644000000000000000000000316414452563620015661 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-01-28 23:24+0000\n" "Language-Team: none\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "キーの発行は無効" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG でのエラー" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "キーがありません。生成しましょう!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "キーを選択" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "読み込んでいます…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "GnuPG に問い合わせています" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "キーはキーチェーンにありません" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "暗号化" dino-0.4.3/plugins/openpgp/po/ko.po0000644000000000000000000000310014452563620015666 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2022-10-17 11:49+0000\n" "Language-Team: none\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.15-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 #, fuzzy msgid "Key publishing disabled" msgstr "키 게재 비활성화됨" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG에 에러가 있음" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "사용 가능한 키 없음. 하나 생성하세요!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "키 선택" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "로딩중…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "키가 키체인에 있지 않음" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "암호화" dino-0.4.3/plugins/openpgp/po/lb.po0000644000000000000000000000302214452563620015655 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-01-24 12:20+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.19-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Schlëssel Verëffentlechung ausgeschalt" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Feeler am GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Keng Schlëssele verfügbar. Generéier een!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Schlëssel auswielen" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Lueden…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "GnuPG gëtt ofgefrot" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Schlësselen net am Schlësselbond" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Verschlësselung" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.4.3/plugins/openpgp/po/lt.po0000644000000000000000000000327514452563620015711 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-02-01 15:50+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > " "19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? " "1 : 2);\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Rakto paskelbimas išjungtas" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Klaida GnuPG programoje" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nėra prieinamų raktų. Sugeneruokite!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Pasirinkti raktą" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Įkeliama…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Užklausiama GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Rakto nėra raktinėje" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Šifravimas" dino-0.4.3/plugins/openpgp/po/lv.po0000644000000000000000000000250614452563620015707 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Language-Team: none\n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "" dino-0.4.3/plugins/openpgp/po/nb.po0000644000000000000000000000327214452563620015666 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2017-11-18 00:23+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.18-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Nøkkelpublisering avskrudd" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Feil i GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Ingen nøkler tilgjengelige. Generer én." #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Velg nøkkel" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Laster…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Sender spørring til GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Nøkkelen finnes ikke i nøkkelknippet" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Kryptering" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.4.3/plugins/openpgp/po/nl.po0000644000000000000000000000300114452563620015666 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-09-05 19:21+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Sleutelpublicatie uitgeschakeld" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Fout in GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Geen sleutels beschikbaar. Genereer een sleutel!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Kies een sleutel" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Bezig met laden…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Bezig met aanroepen van GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Sleutel niet in sleutelbos" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Versleuteling" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.4.3/plugins/openpgp/po/oc.po0000644000000000000000000000311114452563620015660 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-06-22 10:41+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publicacion de clau desactivada" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Error dins gnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Cap de clau pas disponibla. Generatz-ne una !" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Seleccionar una clau" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Cargament…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Consultacion de GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "La clau es pas al trocèl" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Chiframent" dino-0.4.3/plugins/openpgp/po/pl.po0000644000000000000000000000326714452563620015706 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-05-13 18:41+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publikowanie kluczy wyłączone" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Błąd w GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Brak kluczy. Wygeneruj nowy!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Wybierz klucz" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Ładowanie…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Odpytuję GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Klucz nie znajduje się w pęku kluczy" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Szyfrowanie" dino-0.4.3/plugins/openpgp/po/pt.po0000644000000000000000000000307614452563620015714 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-08-23 21:36+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publicação da chave desativada" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Erro no GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nenhuma chave disponível. Gere uma!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Selecionar chave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Carregando…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "A chave não está no chaveiro" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Criptografia" dino-0.4.3/plugins/openpgp/po/pt_BR.po0000644000000000000000000000310014452563620016263 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-01-16 00:21+0000\n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publicação da chave desativada" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Erro no GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nenhuma chave disponível. Gere uma!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Selecionar chave" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Carregando…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "A chave não está no chaveiro" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Criptografia" dino-0.4.3/plugins/openpgp/po/ro.po0000644000000000000000000000327614452563620015713 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-01-12 23:14+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 2.19-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Cheie publică dezactivată" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Eroare în GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Nici o cheie disponibilă. Generați una!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Selectați cheia" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Se încarcă…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Se interoghează GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Cheia nu este în șirul de chei" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Criptare" dino-0.4.3/plugins/openpgp/po/ru.po0000644000000000000000000000351114452563620015711 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-06-02 11:41+0000\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Публикация ключа отключена" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Ошибка в GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Нет доступных ключей. Стоило бы сгенерировать один!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Выбрать ключ" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Загрузка…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Запрос GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Отсутствует ключ в связке" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Шифрование" dino-0.4.3/plugins/openpgp/po/sq.po0000644000000000000000000000310514452563620015705 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2021-03-04 15:20+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Publikim kyçesh i çaktivizuar" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Gabim në GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "S’ka kyçe gati. Prodhoni një të tillë!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Përzgjidhni kyç" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Po ngarkohet…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Po pyetet GnuPG-ja" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Kyç jo në varg kyçesh" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Fshehtëzim" dino-0.4.3/plugins/openpgp/po/sv.po0000644000000000000000000000306214452563620015714 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2019-08-14 11:23+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.8-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Nyckelpublicering inaktiverad" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "Fel i GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Inga nycklar tillgängliga. Generera en!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Välj nyckel" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Laddar…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "Frågar GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Nyckeln finns inte i nyckelknippan" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Kryptering" dino-0.4.3/plugins/openpgp/po/tr.po0000644000000000000000000000306014452563620015707 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-02-04 16:50+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "Anahtar yayınlama devre dışı" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG'de Hata" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "Anahtar yok. Bir tane oluşturun!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Anahtar seçin" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Yükleniyor…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "GnuPG sorgu" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "Anahtar anahtarlıkta değil" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Şifreleme" dino-0.4.3/plugins/openpgp/po/uk.po0000644000000000000000000000327514452563620015711 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-12-31 17:29+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 #, fuzzy msgid "Key publishing disabled" msgstr "Публікація ключа відключена" #: plugins/openpgp/src/account_settings_entry.vala:68 #, fuzzy msgid "Error in GnuPG" msgstr "Помилка у GnuPG" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "Вибрати ключ" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "Завантаження…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:28 #, fuzzy msgid "Key not in keychain" msgstr "Відсутній ключ у зв’язці" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "Шифрування" dino-0.4.3/plugins/openpgp/po/zh_CN.po0000644000000000000000000000322114452563620016262 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2018-05-08 09:42+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_Hans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 3.0-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "已禁用密钥发布功能" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG 错误" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "没有密钥可用。生成一个吧!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "选择密钥" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "载入中…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "正在查询 GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "密钥并没有在密钥串里" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "加密" #~ msgid "Own fingerprint" #~ msgstr "自己的指纹" #~ msgid "Will be generated on first connection" #~ msgstr "会在第一次连接时生成" #~ msgid "Database error" #~ msgstr "数据库错误" dino-0.4.3/plugins/openpgp/po/zh_TW.po0000644000000000000000000000317514452563620016324 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-07 21:31+0100\n" "PO-Revision-Date: 2020-11-24 05:28+0000\n" "Language-Team: Chinese (Traditional) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Key publishing disabled" msgstr "金鑰發佈已經被禁用" #: plugins/openpgp/src/account_settings_entry.vala:68 msgid "Error in GnuPG" msgstr "GnuPG 錯誤" #: plugins/openpgp/src/account_settings_entry.vala:72 msgid "No keys available. Generate one!" msgstr "沒有可用的金鑰。生成一個!" #: plugins/openpgp/src/account_settings_entry.vala:101 msgid "Select key" msgstr "選擇金鑰" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Loading…" msgstr "載入中…" #: plugins/openpgp/src/account_settings_entry.vala:114 msgid "Querying GnuPG" msgstr "正在查詢 GnuPG" #: plugins/openpgp/src/contact_details_provider.vala:28 msgid "Key not in keychain" msgstr "密鑰不在密鑰串中" #: plugins/openpgp/src/contact_details_provider.vala:30 msgid "Encryption" msgstr "加密" dino-0.4.3/plugins/openpgp/src/0000755000000000000000000000000014452563620015074 5ustar rootrootdino-0.4.3/plugins/openpgp/src/account_settings_entry.vala0000644000000000000000000001242414452563620022541 0ustar rootrootusing Dino.Entities; using Gtk; namespace Dino.Plugins.OpenPgp { public class AccountSettingsEntry : Plugins.AccountSettingsEntry { private Label label; private Button button; private ComboBox combobox; private Stack stack; private Plugin plugin; private Account current_account; private Gee.List keys = null; private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(string?)); public override string id { get { return "pgp_key_picker"; }} public override string name { get { return "OpenPGP"; }} public AccountSettingsEntry(Plugin plugin) { this.plugin = plugin; Builder builder = new Builder.from_resource("/im/dino/Dino/openpgp/account_settings_item.ui"); stack = (Stack) builder.get_object("stack"); label = (Label) builder.get_object("label"); button = (Button) builder.get_object("button"); combobox = (ComboBox) builder.get_object("combobox"); CellRendererText renderer = new CellRendererText(); renderer.set_padding(0, 0); combobox.pack_start(renderer, true); combobox.add_attribute(renderer, "markup", 0); combobox.set_model(list_store); button.clicked.connect(on_button_clicked); combobox.changed.connect(key_changed); } public override void deactivate() { stack.set_visible_child_name("label"); } public override void set_account(Account account) { set_account_.begin(account); } private async void set_account_(Account account) { this.current_account = account; if (keys == null) { yield fetch_keys(); populate_list_store(); } activate_current_account(); } private void on_button_clicked() { activated(); stack.set_visible_child_name("entry"); combobox.grab_focus(); combobox.popup(); } private void activate_current_account() { combobox.changed.disconnect(key_changed); if (keys == null) { label.set_markup(build_markup_string(_("Key publishing disabled"), _("Error in GnuPG"))); return; } if (keys.size == 0) { label.set_markup(build_markup_string(_("Key publishing disabled"), _("No keys available. Generate one!"))); return; } string? account_key = plugin.db.get_account_key(current_account); int activate_index = 0; for (int i = 0; i < keys.size; i++) { GPG.Key key = keys[i]; if (key.fpr == account_key) { activate_index = i + 1; } } combobox.active = activate_index; TreeIter selected; combobox.get_active_iter(out selected); set_label_active(selected); combobox.changed.connect(key_changed); } private void populate_list_store() { if (keys == null || keys.size == 0) { return; } list_store.clear(); TreeIter iter; list_store.append(out iter); list_store.set(iter, 0, build_markup_string(_("Key publishing disabled"), _("Select key") + " \n "), 1, ""); for (int i = 0; i < keys.size; i++) { list_store.append(out iter); list_store.set(iter, 0, @"$(Markup.escape_text(keys[i].uids[0].uid))\n$(markup_colorize_id(keys[i].fpr, true)) "); list_store.set(iter, 1, keys[i].fpr); if (keys[i].fpr == plugin.db.get_account_key(current_account)) { set_label_active(iter, i + 1); } } button.sensitive = true; } private async void fetch_keys() { label.set_markup(build_markup_string(_("Loading…"), _("Querying GnuPG"))); SourceFunc callback = fetch_keys.callback; new Thread (null, () => { // Querying GnuPG might take some time try { keys = GPGHelper.get_keylist(null, true); } catch (Error e) { } Idle.add((owned)callback); return null; }); yield; } private void set_label_active(TreeIter iter, int i = -1) { Value text; list_store.get_value(iter, 0, out text); label.set_markup((string) text); if (i != -1) combobox.active = i; } private void key_changed() { TreeIter selected; bool iter_valid = combobox.get_active_iter(out selected); if (iter_valid) { Value key_value; list_store.get_value(selected, 1, out key_value); string? key_id = key_value as string; if (key_id != null) { if (plugin.modules.has_key(current_account)) { plugin.modules[current_account].set_private_key_id(key_id); } plugin.db.set_account_key(current_account, key_id); } set_label_active(selected); deactivate(); } } private string build_markup_string(string primary, string secondary) { return @"$(Markup.escape_text(primary))\n$secondary"; } public override Object? get_widget(WidgetType type) { if (type != WidgetType.GTK4) return null; return stack; } } }dino-0.4.3/plugins/openpgp/src/contact_details_provider.vala0000644000000000000000000000260014452563620023011 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Plugins.OpenPgp { public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "pgp_info"; } } private StreamInteractor stream_interactor; public ContactDetailsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); if (key_id != null) { Label label = new Label("") { use_markup=true, justify=Justification.RIGHT, selectable=true }; Gee.List? keys = null; try { keys = GPGHelper.get_keylist(key_id); } catch (Error e) { } if (keys != null && keys.size > 0) { label.label = markup_colorize_id(keys[0].fpr, true); } else { label.label = _("Key not in keychain") + "\n" + markup_colorize_id(key_id, false); } contact_details.add(_("Encryption"), "OpenPGP", "", label); } } } } } dino-0.4.3/plugins/openpgp/src/database.vala0000644000000000000000000000473014452563620017511 0ustar rootrootusing Qlite; using Dino.Entities; using Xmpp; namespace Dino.Plugins.OpenPgp { public class Database : Qlite.Database { private const int VERSION = 0; public class AccountSetting : Table { public Column account_id = new Column.Integer("account_id") { primary_key = true }; public Column key = new Column.Text("key") { not_null = true }; internal AccountSetting(Database db) { base(db, "account_setting"); init({account_id, key}); } } public class ContactKey : Table { public Column jid = new Column.Text("jid") { primary_key = true }; public Column key = new Column.Text("key") { not_null = true }; internal ContactKey(Database db) { base(db, "contact_key"); init({jid, key}); } } public AccountSetting account_setting_table { get; private set; } public ContactKey contact_key_table { get; private set; } public Database(string filename) { base(filename, VERSION); this.account_setting_table = new AccountSetting(this); this.contact_key_table = new ContactKey(this); init({account_setting_table, contact_key_table}); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set OpenPGP database properties: %s", e.message); } } public void set_contact_key(Jid jid, string key) { contact_key_table.upsert() .value(contact_key_table.jid, jid.to_string(), true) .value(contact_key_table.key, key) .perform(); } public string? get_contact_key(Jid jid) { return contact_key_table.select({contact_key_table.key}) .with(contact_key_table.jid, "=", jid.to_string())[contact_key_table.key]; } public void set_account_key(Account account, string key) { account_setting_table.upsert() .value(account_setting_table.account_id, account.id, true) .value(account_setting_table.key, key) .perform(); } public string? get_account_key(Account account) { return account_setting_table.select({account_setting_table.key}) .with(account_setting_table.account_id, "=", account.id)[account_setting_table.key]; } public override void migrate(long oldVersion) { } } } dino-0.4.3/plugins/openpgp/src/encryption_list_entry.vala0000644000000000000000000000633714452563620022420 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Plugins.OpenPgp { private class EncryptionListEntry : Plugins.EncryptionListEntry, Object { private StreamInteractor stream_interactor; private Database db; public EncryptionListEntry(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public Entities.Encryption encryption { get { return Encryption.PGP; }} public string name { get { return "OpenPGP"; }} public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { return null; } public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { return null; } public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { try { GPGHelper.get_public_key(db.get_account_key(conversation.account) ?? ""); } catch (Error e) { input_status_callback(new Plugins.InputFieldStatus("You didn't configure OpenPGP for this account. You can do that in the Accounts Dialog.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } if (conversation.type_ == Conversation.Type.CHAT) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); if (key_id == null) { input_status_callback(new Plugins.InputFieldStatus("This contact does not support %s encryption.".printf("OpenPGP"), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } try { GPGHelper.get_keylist(key_id); } catch (Error e) { input_status_callback(new Plugins.InputFieldStatus("This contact's OpenPGP key is not in your keyring.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); } } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { Gee.List muc_jids = new Gee.ArrayList(); Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(occupants); Gee.List? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account); if (offline_members != null) muc_jids.add_all(offline_members); foreach (Jid jid in muc_jids) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, jid); if (key_id == null) { input_status_callback(new Plugins.InputFieldStatus("A member's OpenPGP key is not in your keyring: %s / %s.".printf(jid.to_string(), key_id), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } } } } } } dino-0.4.3/plugins/openpgp/src/file_transfer/0000755000000000000000000000000014452563620017717 5ustar rootrootdino-0.4.3/plugins/openpgp/src/file_transfer/file_decryptor.vala0000644000000000000000000000425414452563620023603 0ustar rootrootusing Dino.Entities; namespace Dino.Plugins.OpenPgp { public class PgpFileDecryptor : FileDecryptor, Object { public Encryption get_encryption() { return Encryption.PGP; } public FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { return receive_data; } public FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { return file_meta; } public bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { return file_transfer.file_name.has_suffix("pgp") || file_transfer.mime_type == "application/pgp-encrypted"; } public async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError { try { uint8[] buf = new uint8[256]; ByteArray data = new ByteArray(); size_t len = -1; do { len = yield encrypted_stream.read_async(buf); data.append(buf[0:len]); } while(len > 0); GPGHelper.DecryptedData clear_data = GPGHelper.decrypt_data(data.data); file_transfer.encryption = Encryption.PGP; if (clear_data.filename != null && clear_data.filename != "") { debug("Decrypting file %s from %s", clear_data.filename, file_transfer.file_name); file_transfer.file_name = clear_data.filename; } else if (file_transfer.file_name.has_suffix(".pgp")) { debug("Decrypting file %s from %s", file_transfer.file_name.substring(0, file_transfer.file_name.length - 4), file_transfer.file_name); file_transfer.file_name = file_transfer.file_name.substring(0, file_transfer.file_name.length - 4); } return new MemoryInputStream.from_data(clear_data.data, GLib.free); } catch (Error e) { throw new FileReceiveError.DECRYPTION_FAILED("PGP file decryption error: %s".printf(e.message)); } } } } dino-0.4.3/plugins/openpgp/src/file_transfer/file_encryptor.vala0000644000000000000000000000336614452563620023620 0ustar rootrootusing Dino.Entities; namespace Dino.Plugins.OpenPgp { public class PgpFileEncryptor : Dino.FileEncryptor, Object { StreamInteractor stream_interactor; public PgpFileEncryptor(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer) { return conversation.encryption == Encryption.PGP; } public FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError { FileMeta file_meta = new FileMeta(); try { GPG.Key[] keys = stream_interactor.get_module(Manager.IDENTITY).get_key_fprs(conversation); uint8[] enc_content = GPGHelper.encrypt_file(file_transfer.get_file().get_path(), keys, GPG.EncryptFlags.ALWAYS_TRUST, file_transfer.file_name); file_transfer.input_stream = new MemoryInputStream.from_data(enc_content, GLib.free); file_transfer.encryption = Encryption.PGP; file_transfer.server_file_name = Xmpp.random_uuid() + ".pgp"; file_meta.size = enc_content.length; } catch (Error e) { throw new FileSendError.ENCRYPTION_FAILED("PGP file encryption error: %s".printf(e.message)); } debug("Encrypting file %s as %s", file_transfer.file_name, file_transfer.server_file_name); return file_meta; } public FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return null; send_data.encrypt_message = false; return file_send_data; } } } dino-0.4.3/plugins/openpgp/src/manager.vala0000644000000000000000000001205214452563620017353 0ustar rootrootusing Gee; using Xmpp; using Xmpp; using Dino.Entities; namespace Dino.Plugins.OpenPgp { public class Manager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("pgp_manager"); public string id { get { return IDENTITY.id; } } public const string MESSAGE_ENCRYPTED = "pgp"; private StreamInteractor stream_interactor; private Database db; private HashMap pgp_key_ids = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); private ReceivedMessageListener received_message_listener = new ReceivedMessageListener(); public static void start(StreamInteractor stream_interactor, Database db) { Manager m = new Manager(stream_interactor, db); stream_interactor.add_module(m); } private Manager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_send.connect(check_encypt); } public GPG.Key[] get_key_fprs(Conversation conversation) throws Error { Gee.List keys = new Gee.ArrayList(); keys.add(db.get_account_key(conversation.account)); if (conversation.type_ == Conversation.Type.GROUPCHAT) { Gee.List muc_jids = new Gee.ArrayList(); Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(occupants); Gee.List? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(offline_members); foreach (Jid jid in muc_jids) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, jid); if (key_id != null && GPGHelper.get_keylist(key_id).size > 0 && !keys.contains(key_id)) { keys.add(key_id); } } } else { string? key_id = get_key_id(conversation.account, conversation.counterpart); if (key_id != null) { keys.add(key_id); } } GPG.Key[] gpgkeys = new GPG.Key[keys.size]; for (int i = 0; i < keys.size; i++) { try { GPG.Key key = GPGHelper.get_public_key(keys[i]); if (key != null) gpgkeys[i] = key; } catch (Error e) {} } return gpgkeys; } private void check_encypt(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { try { if (message.encryption == Encryption.PGP) { GPG.Key[] keys = get_key_fprs(conversation); XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { bool encrypted = stream.get_module(Module.IDENTITY).encrypt(message_stanza, keys); if (!encrypted) message.marked = Entities.Message.Marked.WONTSEND; } } } catch (Error e) { message.marked = Entities.Message.Marked.WONTSEND; } } public string? get_key_id(Account account, Jid jid) { Jid search_jid = stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account) ? jid : jid.bare_jid; return db.get_contact_key(search_jid); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Module.IDENTITY).received_jid_key_id.connect((stream, jid, key_id) => { on_jid_key_received(account, jid, key_id); }); } private void on_jid_key_received(Account account, Jid jid, string key_id) { lock (pgp_key_ids) { if (!pgp_key_ids.has_key(jid) || pgp_key_ids[jid] != key_id) { Jid set_jid = stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account) ? jid : jid.bare_jid; db.set_contact_key(set_jid, key_id); } pgp_key_ids[jid] = key_id; } } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "DECRYPT"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (MessageFlag.get_flag(stanza) != null && MessageFlag.get_flag(stanza).decrypted) { message.encryption = Encryption.PGP; } return false; } } } } dino-0.4.3/plugins/openpgp/src/plugin.vala0000644000000000000000000000407214452563620017242 0ustar rootrootusing Gee; using Dino.Entities; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.OpenPgp { public class Plugin : Plugins.RootInterface, Object { public Dino.Application app; public Database db; public HashMap modules = new HashMap(Account.hash_func, Account.equals_func); private EncryptionListEntry list_entry; private AccountSettingsEntry settings_entry; private ContactDetailsProvider contact_details_provider; public void registered(Dino.Application app) { this.app = app; this.db = new Database(Path.build_filename(Application.get_storage_dir(), "pgp.db")); this.list_entry = new EncryptionListEntry(app.stream_interactor, db); this.settings_entry = new AccountSettingsEntry(this); this.contact_details_provider = new ContactDetailsProvider(app.stream_interactor); app.plugin_registry.register_encryption_list_entry(list_entry); app.plugin_registry.register_account_settings_entry(settings_entry); app.plugin_registry.register_contact_details_entry(contact_details_provider); app.stream_interactor.module_manager.initialize_account_modules.connect(on_initialize_account_modules); Manager.start(app.stream_interactor, db); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_encryptor(new PgpFileEncryptor(app.stream_interactor)); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_decryptor(new PgpFileDecryptor()); JingleFileHelperRegistry.instance.add_encryption_helper(Encryption.PGP, new JingleFileEncryptionHelperTransferOnly()); internationalize(GETTEXT_PACKAGE, app.search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR)); } public void shutdown() { } private void on_initialize_account_modules(Account account, ArrayList modules) { Module module = new Module(db.get_account_key(account)); this.modules[account] = module; modules.add(module); } } } dino-0.4.3/plugins/openpgp/src/register_plugin.vala0000644000000000000000000000013714452563620021144 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.OpenPgp.Plugin); }dino-0.4.3/plugins/openpgp/src/stream_flag.vala0000644000000000000000000000105214452563620020223 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Plugins.OpenPgp { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "pgp"); public HashMap key_ids = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); public string? get_key_id(Jid jid) { return key_ids[jid]; } public void set_key_id(Jid jid, string key) { key_ids[jid] = key; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/plugins/openpgp/src/stream_module.vala0000644000000000000000000001661614452563620020613 0ustar rootrootusing GPG; using Xmpp; namespace Dino.Plugins.OpenPgp { private const string NS_URI = "jabber:x"; private const string NS_URI_ENCRYPTED = NS_URI + ":encrypted"; private const string NS_URI_SIGNED = NS_URI + ":signed"; public class Module : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "0027_current_pgp_usage"); public signal void received_jid_key_id(XmppStream stream, Jid jid, string key_id); private string? signed_status = null; private Key? own_key = null; private ReceivedPipelineDecryptListener received_pipeline_decrypt_listener = new ReceivedPipelineDecryptListener(); public Module(string? own_key_id = null) { set_private_key_id(own_key_id); } public void set_private_key_id(string? own_key_id) { if (own_key_id != null) { try { own_key = GPGHelper.get_private_key(own_key_id); if (own_key == null) warning("Can't get PGP private key"); } catch (Error e) { } if (own_key != null) { signed_status = gpg_sign("", own_key); } } } public bool encrypt(MessageStanza message, GPG.Key[] keys) { string? enc_body = gpg_encrypt(message.body, keys); if (enc_body != null) { message.stanza.put_node(new StanzaNode.build("x", NS_URI_ENCRYPTED).add_self_xmlns().put_node(new StanzaNode.text(enc_body))); message.body = "[This message is OpenPGP encrypted (see XEP-0027)]"; Xep.ExplicitEncryption.add_encryption_tag_to_message(message, NS_URI_ENCRYPTED); return true; } return false; } public override void attach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).received_presence.connect(on_received_presence); stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); stream.get_module(MessageModule.IDENTITY).received_pipeline.connect(received_pipeline_decrypt_listener); stream.add_flag(new Flag()); } public override void detach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).received_presence.disconnect(on_received_presence); stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); stream.get_module(MessageModule.IDENTITY).received_pipeline.disconnect(received_pipeline_decrypt_listener); } public static void require(XmppStream stream) { if (stream.get_module(IDENTITY) == null) stream.add_module(new Module()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private void on_received_presence(XmppStream stream, Presence.Stanza presence) { StanzaNode x_node = presence.stanza.get_subnode("x", NS_URI_SIGNED); if (x_node == null) return; string? sig = x_node.get_string_content(); if (sig == null) return; new Thread (null, () => { string signed_data = presence.status == null ? "" : presence.status; string? key_id = get_sign_key(sig, signed_data); if (key_id != null) { stream.get_flag(Flag.IDENTITY).set_key_id(presence.from, key_id); Idle.add(() => { received_jid_key_id(stream, presence.from, key_id); return false; }); } return null; }); } private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE && signed_status != null) { presence.stanza.put_node(new StanzaNode.build("x", NS_URI_SIGNED).add_self_xmlns().put_node(new StanzaNode.text(signed_status))); } } private static string? gpg_encrypt(string plain, GPG.Key[] keys) { string encr; try { encr = GPGHelper.encrypt_armor(plain, keys, GPG.EncryptFlags.ALWAYS_TRUST); } catch (Error e) { return null; } int encryption_start = encr.index_of("\n\n") + 2; return encr.substring(encryption_start, encr.length - "\n-----END PGP MESSAGE-----".length - encryption_start); } private static string? get_sign_key(string sig, string signed_text) { string armor = "-----BEGIN PGP MESSAGE-----\n\n" + sig + "\n-----END PGP MESSAGE-----"; string? sign_key = null; try { sign_key = GPGHelper.get_sign_key(armor, signed_text); } catch (Error e) { } return sign_key; } private static string? gpg_sign(string str, Key key) { string signed; try { signed = GPGHelper.sign(str, GPG.SigMode.CLEAR, key); } catch (Error e) { return null; } int signature_start = signed.index_of("-----BEGIN PGP SIGNATURE-----"); signature_start = signed.index_of("\n\n", signature_start) + 2; return signed.substring(signature_start, signed.length - "\n-----END PGP SIGNATURE-----".length - signature_start); } } public class MessageFlag : Xmpp.MessageFlag { public const string id = "pgp"; public bool decrypted = false; public static MessageFlag? get_flag(MessageStanza message) { return (MessageFlag) message.get_flag(NS_URI, id); } public override string get_ns() { return NS_URI; } public override string get_id() { return id; } } public class ReceivedPipelineDecryptListener : StanzaListener { private string[] after_actions_const = {"MODIFY_BODY"}; public override string action_group { get { return "ENCRYPT_BODY"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(XmppStream stream, MessageStanza message) { string? encrypted = get_cyphertext(message); if (encrypted != null) { MessageFlag flag = new MessageFlag(); message.add_flag(flag); string? decrypted = yield gpg_decrypt(encrypted); if (decrypted != null) { flag.decrypted = true; message.body = decrypted; } } return false; } private static async string? gpg_decrypt(string enc) { SourceFunc callback = gpg_decrypt.callback; string? res = null; new Thread (null, () => { string armor = "-----BEGIN PGP MESSAGE-----\n\n" + enc + "\n-----END PGP MESSAGE-----"; try { res = GPGHelper.decrypt(armor); } catch (Error e) { res = null; } Idle.add((owned) callback); return null; }); yield; return res; } private string? get_cyphertext(MessageStanza message) { StanzaNode? x_node = message.stanza.get_subnode("x", NS_URI_ENCRYPTED); return x_node == null ? null : x_node.get_string_content(); } } } dino-0.4.3/plugins/openpgp/src/util.vala0000644000000000000000000000312614452563620016720 0ustar rootrootusing Gtk; using Dino.Entities; using Xmpp.Util; namespace Dino.Plugins.OpenPgp { /* Adapted from OpenKeychain */ public static string markup_colorize_id(string s, bool is_fingerprint) { string markup = is_fingerprint ? "" : "0x"; for (int i = 0; i < s.length; i += 4) { string four_chars = s.substring(i, 4).down(); int raw = (int) from_hex(four_chars); uint8[] bytes = {(uint8) ((raw >> 8) & 0xff - 128), (uint8) (raw & 0xff - 128)}; Checksum checksum = new Checksum(ChecksumType.SHA1); checksum.update(bytes, bytes.length); uint8[] digest = new uint8[20]; size_t len = 20; checksum.get_digest(digest, ref len); uint8 r = digest[0]; uint8 g = digest[1]; uint8 b = digest[2]; if (r == 0 && g == 0 && b == 0) r = g = b = 1; double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b; if (brightness < 80) { double factor = 80.0 / brightness; r = uint8.min(255, (uint8) (r * factor)); g = uint8.min(255, (uint8) (g * factor)); b = uint8.min(255, (uint8) (b * factor)); } else if (brightness > 180) { double factor = 180.0 / brightness; r = (uint8) (r * factor); g = (uint8) (g * factor); b = (uint8) (b * factor); } if (i == 4 * 5) markup += "\n"; markup += @"$four_chars"; if (is_fingerprint) markup += " "; } return "" + markup + ""; } } dino-0.4.3/plugins/rtp/0000755000000000000000000000000014452563620013442 5ustar rootrootdino-0.4.3/plugins/rtp/CMakeLists.txt0000644000000000000000000000552114452563620016205 0ustar rootrootfind_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED) find_package(WebRTCAudioProcessing 0.2) find_packages(RTP_PACKAGES REQUIRED Gee GLib GModule GnuTLS GObject GTK4 Gst GstApp GstAudio GstRtp GstVideo ) set(RTP_DEFINITIONS) set(RTP_EXTRA_OPTIONS) if(GstRtp_VERSION VERSION_GREATER_EQUAL "1.16") set(RTP_DEFINITIONS ${RTP_DEFINITIONS} GST_1_16) endif() if(GstRtp_VERSION VERSION_GREATER_EQUAL "1.18") set(RTP_DEFINITIONS ${RTP_DEFINITIONS} GST_1_18) endif() if(GstRtp_VERSION VERSION_GREATER_EQUAL "1.20") set(RTP_DEFINITIONS ${RTP_DEFINITIONS} GST_1_20) endif() if(NOT VALA_VERSION VERSION_GREATER_EQUAL "0.56.1") set(RTP_EXTRA_OPTIONS ${RTP_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) endif() set(RTP_ENABLE_VP9 "no" CACHE BOOL "Enable VP9 support") if(RTP_ENABLE_VP9) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_VP9) endif() set(RTP_ENABLE_H264 "no" CACHE BOOL "Enable H264 support") if(RTP_ENABLE_H264) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_H264) endif() set(RTP_ENABLE_VAAPI "no" CACHE BOOL "Enable VAAPI support") if(RTP_ENABLE_VAAPI) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_VAAPI) endif() set(RTP_ENABLE_MSDK "no" CACHE BOOL "Enable MSDK support") if(RTP_ENABLE_MSDK) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_MSDK) endif() if(WebRTCAudioProcessing_VERSION GREATER "0.4") message(STATUS "Ignoring WebRTCAudioProcessing, only versions < 0.4 supported so far") unset(WebRTCAudioProcessing_FOUND) endif() if(WebRTCAudioProcessing_FOUND) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} WITH_VOICE_PROCESSOR) set(RTP_VOICE_PROCESSOR_VALA src/voice_processor.vala) set(RTP_VOICE_PROCESSOR_CXX src/voice_processor_native.cpp) set(RTP_VOICE_PROCESSOR_LIB webrtc-audio-processing) else() message(STATUS "WebRTCAudioProcessing not found, build without voice pre-processing!") endif() vala_precompile(RTP_VALA_C SOURCES src/codec_util.vala src/device.vala src/module.vala src/plugin.vala src/stream.vala src/video_widget.vala src/register_plugin.vala ${RTP_VOICE_PROCESSOR_VALA} CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi PACKAGES ${RTP_PACKAGES} DEFINITIONS ${RTP_DEFINITIONS} OPTIONS ${RTP_EXTRA_OPTIONS} ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="rtp" -I${CMAKE_CURRENT_SOURCE_DIR}/src) add_library(rtp SHARED ${RTP_VALA_C} ${RTP_VOICE_PROCESSOR_CXX} src/gst_fixes.c) target_link_libraries(rtp libdino crypto-vala ${RTP_PACKAGES} gstreamer-rtp-1.0 ${RTP_VOICE_PROCESSOR_LIB}) set_target_properties(rtp PROPERTIES PREFIX "") set_target_properties(rtp PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) install(TARGETS rtp ${PLUGIN_INSTALL}) dino-0.4.3/plugins/rtp/src/0000755000000000000000000000000014452563620014231 5ustar rootrootdino-0.4.3/plugins/rtp/src/codec_util.vala0000644000000000000000000004352314452563620017217 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.CodecUtil { private Set supported_elements = new HashSet(); private Set unsupported_elements = new HashSet(); public static Gst.Caps get_caps(string media, JingleRtp.PayloadType payload_type, bool incoming) { Gst.Caps caps = new Gst.Caps.simple("application/x-rtp", "media", typeof(string), media, "payload", typeof(int), payload_type.id); //"channels", typeof(int), payloadType.channels, //"max-ptime", typeof(int), payloadType.maxptime); unowned Gst.Structure s = caps.get_structure(0); if (payload_type.clockrate != 0) { s.set("clock-rate", typeof(int), payload_type.clockrate); } if (payload_type.name != null) { s.set("encoding-name", typeof(string), payload_type.name.up()); } if (incoming) { foreach (JingleRtp.RtcpFeedback rtcp_fb in payload_type.rtcp_fbs) { if (rtcp_fb.subtype == null) { s.set(@"rtcp-fb-$(rtcp_fb.type_)", typeof(bool), true); } else { s.set(@"rtcp-fb-$(rtcp_fb.type_)-$(rtcp_fb.subtype)", typeof(bool), true); } } } return caps; } public static string? get_codec_from_payload(string media, JingleRtp.PayloadType payload_type) { if (payload_type.name != null) return payload_type.name.down(); if (media == "audio") { switch (payload_type.id) { case 0: return "pcmu"; case 8: return "pcma"; } } return null; } public static string? get_media_type_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_media_type(media, get_codec_from_payload(media, payload_type)); } public static string? get_media_type(string media, string? codec) { if (codec == null) return null; if (media == "audio") { switch (codec) { case "pcma": return "audio/x-alaw"; case "pcmu": return "audio/x-mulaw"; } } return @"$media/x-$codec"; } public static string? get_rtp_pay_element_name_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_pay_candidate(media, get_codec_from_payload(media, payload_type)); } public static string? get_pay_candidate(string media, string? codec) { if (codec == null) return null; return @"rtp$(codec)pay"; } public static string? get_rtp_depay_element_name_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_depay_candidate(media, get_codec_from_payload(media, payload_type)); } public static string? get_depay_candidate(string media, string? codec) { if (codec == null) return null; return @"rtp$(codec)depay"; } public static string[] get_encode_candidates(string media, string? codec) { if (codec == null) return new string[0]; if (media == "audio") { switch (codec) { case "opus": return new string[] {"opusenc"}; case "speex": return new string[] {"speexenc"}; case "pcma": return new string[] {"alawenc"}; case "pcmu": return new string[] {"mulawenc"}; case "g722": return new string[] {"avenc_g722"}; } } else if (media == "video") { switch (codec) { case "h264": return new string[] { #if ENABLE_MSDK "msdkh264enc", #endif #if ENABLE_VAAPI "vaapih264enc", #endif "x264enc" }; case "vp9": return new string[] { #if ENABLE_MSDK "msdkvp9enc", #endif #if ENABLE_VAAPI "vaapivp9enc", #endif "vp9enc" }; case "vp8": return new string[] { #if ENABLE_MSDK "msdkvp8enc", #endif #if ENABLE_VAAPI "vaapivp8enc", #endif "vp8enc" }; } } return new string[0]; } public static string[] get_decode_candidates(string media, string? codec) { if (codec == null) return new string[0]; if (media == "audio") { switch (codec) { case "opus": return new string[] {"opusdec"}; case "speex": return new string[] {"speexdec"}; case "pcma": return new string[] {"alawdec"}; case "pcmu": return new string[] {"mulawdec"}; case "g722": return new string[] {"avdec_g722"}; } } else if (media == "video") { switch (codec) { case "h264": return new string[] { #if ENABLE_MSDK "msdkh264dec", #endif #if ENABLE_VAAPI "vaapih264dec", #endif null }; case "vp9": return new string[] { #if ENABLE_MSDK "msdkvp9dec", #endif #if ENABLE_VAAPI "vaapivp9dec", #endif "vp9dec" }; case "vp8": return new string[] { #if ENABLE_MSDK "msdkvp8dec", #endif #if ENABLE_VAAPI "vaapivp8dec", #endif "vp8dec" }; } } return new string[0]; } public static string? get_encode_prefix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { if (encode == "msdkh264enc") return "capsfilter caps=video/x-raw,format=NV12 ! "; if (encode == "vaapih264enc") return "capsfilter caps=video/x-raw,format=NV12 ! "; return null; } public static string? get_encode_args(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { // H264 if (encode == "msdkh264enc") return @" rate-control=vbr"; if (encode == "vaapih264enc") return @" rate-control=vbr"; if (encode == "x264enc") return @" byte-stream=1 speed-preset=ultrafast tune=zerolatency bframes=0 cabac=false dct8x8=false"; // VP8 if (encode == "vaapivp8enc" || encode == "msdkvp8enc") return " rate-control=vbr target-percentage=90"; if (encode == "vp8enc") return " deadline=1 error-resilient=3 lag-in-frames=0 resize-allowed=true threads=8 dropframe-threshold=30 end-usage=vbr cpu-used=4"; // VP9 if (encode == "msdkvp9enc" || encode == "vaapivp9enc") return " rate-control=vbr target-percentage=90"; if (encode == "vp9enc") return " deadline=1 error-resilient=3 lag-in-frames=0 resize-allowed=true threads=8 dropframe-threshold=30 end-usage=vbr cpu-used=4"; // OPUS if (encode == "opusenc") { if (payload_type != null && payload_type.parameters.has("useinbandfec", "1")) return " audio-type=voice inband-fec=true"; return " audio-type=voice"; } return null; } public static string? get_encode_suffix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { // H264 if (media == "video" && codec == "h264") return " ! capsfilter caps=video/x-h264,profile=constrained-baseline ! h264parse"; if (media == "video" && codec == "vp8" && encode == "vp8enc") return " ! capsfilter caps=video/x-vp8,profile=(string)1"; return null; } public uint update_bitrate(string media, JingleRtp.PayloadType payload_type, Gst.Element encode_element, uint bitrate) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return 0; string? codec = get_codec_from_payload(media, payload_type); string? encode_name = get_encode_element_name(media, codec); if (encode_name == null) return 0; Gst.Element encode = encode_bin.get_by_name(@"$(encode_bin.name)_encode"); switch (encode_name) { case "msdkh264enc": case "vaapih264enc": case "x264enc": case "msdkvp9enc": case "vaapivp9enc": case "msdkvp8enc": case "vaapivp8enc": bitrate = uint.min(2048000, bitrate); encode.set("bitrate", bitrate); return bitrate; case "vp9enc": case "vp8enc": bitrate = uint.min(2147483, bitrate); encode.set("target-bitrate", bitrate * 1024); return bitrate; } return 0; } public void update_rescale_caps(Gst.Element encode_element, Gst.Caps caps) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return; Gst.Element rescale_caps = encode_bin.get_by_name(@"$(encode_bin.name)_rescale_caps"); rescale_caps.set("caps", caps); } public Gst.Caps? get_rescale_caps(Gst.Element encode_element) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return null; Gst.Element rescale_caps = encode_bin.get_by_name(@"$(encode_bin.name)_rescale_caps"); Gst.Caps caps; rescale_caps.get("caps", out caps); return caps; } public static string? get_decode_prefix(string media, string codec, string decode, JingleRtp.PayloadType? payload_type) { return null; } public static string? get_decode_args(string media, string codec, string decode, JingleRtp.PayloadType? payload_type) { if (decode == "opusdec" && payload_type != null && payload_type.parameters.has("useinbandfec", "1")) return " use-inband-fec=true"; if (decode == "vaapivp9dec" || decode == "vaapivp8dec" || decode == "vaapih264dec") return " max-errors=100"; if (decode == "vp8dec" || decode == "vp9dec") return " threads=8"; return null; } public static string? get_decode_suffix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { return null; } public static string? get_depay_args(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { #if GST_1_18 if (codec == "vp8") return " wait-for-keyframe=true"; #endif return null; } public bool is_element_supported(string? element_name) { if (element_name == null) return false; if (unsupported_elements.contains(element_name)) return false; if (supported_elements.contains(element_name)) return true; var test_element = Gst.ElementFactory.make(element_name, @"test-$element_name"); if (test_element != null) { supported_elements.add(element_name); return true; } else { warning("%s is not installed or supported on this system", element_name); unsupported_elements.add(element_name); return false; } } public string? get_encode_element_name(string media, string? codec) { if (get_pay_element_name(media, codec) == null) return null; foreach (string candidate in get_encode_candidates(media, codec)) { if (is_element_supported(candidate)) return candidate; } return null; } public string? get_pay_element_name(string media, string? codec) { string? candidate = get_pay_candidate(media, codec); if (candidate != null && is_element_supported(candidate)) return candidate; return null; } public string? get_decode_element_name(string media, string? codec) { if (get_depay_element_name(media, codec) == null) return null; foreach (string candidate in get_decode_candidates(media, codec)) { if (is_element_supported(candidate)) return candidate; } return null; } public string? get_depay_element_name(string media, string? codec) { string? candidate = get_depay_candidate(media, codec); if (candidate != null && is_element_supported(candidate)) return candidate; return null; } public void mark_element_unsupported(string element_name) { unsupported_elements.add(element_name); } public string? get_decode_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode-$codec-$(Random.next_int())"; string? depay = get_depay_element_name(media, codec); string? decode = element_name ?? get_decode_element_name(media, codec); if (depay == null || decode == null) return null; string decode_prefix = get_decode_prefix(media, codec, decode, payload_type) ?? ""; string decode_args = get_decode_args(media, codec, decode, payload_type) ?? ""; string decode_suffix = get_decode_suffix(media, codec, decode, payload_type) ?? ""; string depay_args = get_depay_args(media, codec, decode, payload_type) ?? ""; string resample = media == "audio" ? @" ! audioresample name=$(base_name)_resample" : ""; return @"queue ! $depay$depay_args name=$(base_name)_rtp_depay ! $decode_prefix$decode$decode_args name=$(base_name)_$(codec)_decode$decode_suffix ! $(media)convert name=$(base_name)_convert$resample"; } public Gst.Element? get_decode_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"decode_$(codec)_$(Random.next_int())"; string? desc = get_decode_bin_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to decode %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public string? get_encode_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { string? desc1 = get_encode_bin_without_payloader_description(media, codec, payload_type, element_name, name); string? desc2 = get_payloader_bin_description(media, codec, payload_type, name); return @"$desc1 ! $desc2"; } public string? get_payloader_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? pay = get_pay_element_name(media, codec); if (pay == null) return null; return @"$pay pt=$(payload_type != null ? payload_type.id : 96) name=$(base_name)_rtp_pay"; } public string? get_encode_bin_without_payloader_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? encode = element_name ?? get_encode_element_name(media, codec); if (encode == null) return null; string encode_prefix = get_encode_prefix(media, codec, encode, payload_type) ?? ""; string encode_args = get_encode_args(media, codec, encode, payload_type) ?? ""; string encode_suffix = get_encode_suffix(media, codec, encode, payload_type) ?? ""; string rescale = media == "audio" ? @" ! audioresample name=$(base_name)_resample" : @" ! videoscale name=$(base_name)_rescale ! capsfilter name=$(base_name)_rescale_caps"; return @"$(media)convert name=$(base_name)_convert$rescale ! queue ! $encode_prefix$encode$encode_args name=$(base_name)_encode$encode_suffix"; } public Gst.Element? get_encode_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_encode_bin_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to encode %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public Gst.Element? get_encode_bin_without_payloader(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_encode_bin_without_payloader_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to encode %s %s without payloader: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public Gst.Element? get_payloader_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_payloader_bin_description(media, codec, payload_type, base_name); if (desc == null) return null; debug("Pipeline to payload %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } } dino-0.4.3/plugins/rtp/src/device.vala0000644000000000000000000006257414452563620016353 0ustar rootrootusing Xmpp.Xep.JingleRtp; using Gee; public enum Dino.Plugins.Rtp.DeviceProtocol { OTHER, PIPEWIRE, V4L2, PULSEAUDIO, ALSA } public class Dino.Plugins.Rtp.Device : MediaDevice, Object { private const int[] common_widths = {320, 360, 400, 480, 640, 960, 1280, 1920, 2560, 3840}; public Plugin plugin { get; private set; } public CodecUtil codec_util { get { return plugin.codec_util; } } public Gst.Device device { get; private set; } public string id { owned get { return device_name; }} public string display_name { owned get { return device_display_name; }} public string? detail_name { owned get { if (device.properties.has_field("alsa.card_name")) return device.properties.get_string("alsa.card_name"); if (device.properties.has_field("alsa.name")) return device.properties.get_string("alsa.name"); if (device.properties.has_field("alsa.id")) return device.properties.get_string("alsa.id"); if (device.properties.has_field("api.v4l2.cap.card")) return device.properties.get_string("api.v4l2.cap.card"); return null; }} public string? media { owned get { if (device.has_classes("Audio")) { return "audio"; } else if (device.has_classes("Video")) { return "video"; } else { return null; } }} public bool incoming { get { return is_sink; } } public Gst.Pipeline pipe { get { return plugin.pipe; }} public bool is_source { get { return device.has_classes("Source"); }} public bool is_sink { get { return device.has_classes("Sink"); }} public bool is_monitor { get { return device.properties.get_string("device.class") == "monitor" || (protocol == DeviceProtocol.PIPEWIRE && device.has_classes("Stream")); } } public bool is_default { get { bool ret; device.properties.get_boolean("is-default", out ret); return ret; }} public DeviceProtocol protocol { get { if (device.properties.has_name("pulse-proplist")) return DeviceProtocol.PULSEAUDIO; if (device.properties.has_name("pipewire-proplist")) return DeviceProtocol.PIPEWIRE; if (device.properties.has_name("v4l2deviceprovider")) return DeviceProtocol.V4L2; return DeviceProtocol.OTHER; }} private string device_name; private string device_display_name; private Gst.Caps device_caps; private Gst.Element element; private Gst.Element tee; private Gst.Element dsp; private Gst.Base.Aggregator mixer; private Gst.Element filter; private int links; // Codecs private Gee.Map codecs = new HashMap(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map codec_tees = new HashMap(PayloadType.hash_func, PayloadType.equals_func); // Payloaders private Gee.Map> payloaders = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map> payloader_tees = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map> payloader_links = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); // Bitrate private Gee.Map> codec_bitrates = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private class CodecBitrate { public uint bitrate; public int64 timestamp; public CodecBitrate(uint bitrate) { this.bitrate = bitrate; this.timestamp = get_monotonic_time(); } } public Device(Plugin plugin, Gst.Device device) { this.plugin = plugin; update(device); } public bool matches(Gst.Device device) { if (this.device.name == device.name) return true; return false; } public void update(Gst.Device device) { this.device = device; this.device_name = device.name; this.device_display_name = device.display_name; } public Gst.Element? link_sink() { if (!is_sink) return null; if (element == null) create(); links++; if (mixer != null) { Gst.Element rate = Gst.ElementFactory.make("audiorate", @"$(id)_rate_$(Random.next_int())"); pipe.add(rate); rate.link(mixer); return rate; } if (media == "audio") return filter; return element; } public Gst.Element? link_source(PayloadType? payload_type = null, uint ssrc = 0, int seqnum_offset = -1, uint32 timestamp_offset = 0) { if (!is_source) return null; if (element == null) create(); links++; if (payload_type != null && ssrc != 0 && tee != null) { bool new_codec = false; string? codec = CodecUtil.get_codec_from_payload(media, payload_type); if (!codecs.has_key(payload_type)) { codecs[payload_type] = codec_util.get_encode_bin_without_payloader(media, payload_type, @"$(id)_$(codec)_encoder"); pipe.add(codecs[payload_type]); new_codec = true; } if (!codec_tees.has_key(payload_type)) { codec_tees[payload_type] = Gst.ElementFactory.make("tee", @"$(id)_$(codec)_tee"); codec_tees[payload_type].@set("allow-not-linked", true); pipe.add(codec_tees[payload_type]); codecs[payload_type].link(codec_tees[payload_type]); } if (!payloaders.has_key(payload_type)) { payloaders[payload_type] = new HashMap(); } if (!payloaders[payload_type].has_key(ssrc)) { payloaders[payload_type][ssrc] = codec_util.get_payloader_bin(media, payload_type, @"$(id)_$(codec)_$(ssrc)"); var payload = (Gst.RTP.BasePayload) ((Gst.Bin) payloaders[payload_type][ssrc]).get_by_name(@"$(id)_$(codec)_$(ssrc)_rtp_pay"); payload.ssrc = ssrc; payload.seqnum_offset = seqnum_offset; if (timestamp_offset != 0) { payload.timestamp_offset = timestamp_offset; } pipe.add(payloaders[payload_type][ssrc]); codec_tees[payload_type].link(payloaders[payload_type][ssrc]); debug("Payload for %s with %s using ssrc %u, seqnum_offset %u, timestamp_offset %u", media, codec, ssrc, seqnum_offset, timestamp_offset); } if (!payloader_tees.has_key(payload_type)) { payloader_tees[payload_type] = new HashMap(); } if (!payloader_tees[payload_type].has_key(ssrc)) { payloader_tees[payload_type][ssrc] = Gst.ElementFactory.make("tee", @"$(id)_$(codec)_$(ssrc)_tee"); payloader_tees[payload_type][ssrc].@set("allow-not-linked", true); pipe.add(payloader_tees[payload_type][ssrc]); payloaders[payload_type][ssrc].link(payloader_tees[payload_type][ssrc]); } if (!payloader_links.has_key(payload_type)) { payloader_links[payload_type] = new HashMap(); } if (!payloader_links[payload_type].has_key(ssrc)) { payloader_links[payload_type][ssrc] = 1; } else { payloader_links[payload_type][ssrc] = payloader_links[payload_type][ssrc] + 1; } if (new_codec) { tee.link(codecs[payload_type]); } return payloader_tees[payload_type][ssrc]; } if (tee != null) return tee; return element; } private static double get_target_bitrate(Gst.Caps caps) { if (caps == null || caps.get_size() == 0) return uint.MAX; unowned Gst.Structure? that = caps.get_structure(0); int num = 0, den = 0, width = 0, height = 0; if (!that.has_field("width") || !that.get_int("width", out width)) return uint.MAX; if (!that.has_field("height") || !that.get_int("height", out height)) return uint.MAX; if (!that.has_field("framerate")) return uint.MAX; Value framerate = that.get_value("framerate"); if (framerate.type() != typeof(Gst.Fraction)) return uint.MAX; num = Gst.Value.get_fraction_numerator(framerate); den = Gst.Value.get_fraction_denominator(framerate); double pxs = ((double)num/(double)den) * (double)width * (double)height; double br = Math.sqrt(Math.sqrt(pxs)) * 100.0 - 3700.0; if (br < 128.0) return 128.0; return br; } private Gst.Caps get_active_caps(PayloadType payload_type) { return codec_util.get_rescale_caps(codecs[payload_type]) ?? device_caps; } private void apply_caps(PayloadType payload_type, Gst.Caps caps) { plugin.pause(); debug("Set scaled caps to %s", caps.to_string()); codec_util.update_rescale_caps(codecs[payload_type], caps); plugin.unpause(); } private void apply_width(PayloadType payload_type, int new_width, uint bitrate) { int device_caps_width, device_caps_height, active_caps_width, device_caps_framerate_num, device_caps_framerate_den; device_caps.get_structure(0).get_int("width", out device_caps_width); device_caps.get_structure(0).get_int("height", out device_caps_height); device_caps.get_structure(0).get_fraction("framerate", out device_caps_framerate_num, out device_caps_framerate_den); Gst.Caps active_caps = get_active_caps(payload_type); if (active_caps != null && active_caps.get_size() > 0) { active_caps.get_structure(0).get_int("width", out active_caps_width); } else { active_caps_width = device_caps_width; } if (new_width == active_caps_width) return; int new_height = device_caps_height * new_width / device_caps_width; Gst.Caps new_caps; if (device_caps_framerate_den != 0) { new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null); } else { new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, null); } double required_bitrate = get_target_bitrate(new_caps); debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate); if (bitrate < required_bitrate && new_width > active_caps_width) return; apply_caps(payload_type, new_caps); } public void update_bitrate(PayloadType payload_type, uint bitrate) { if (codecs.has_key(payload_type)) { lock(codec_bitrates); if (!codec_bitrates.has_key(payload_type)) { codec_bitrates[payload_type] = new ArrayList(); } codec_bitrates[payload_type].add(new CodecBitrate(bitrate)); var remove = new ArrayList(); foreach (CodecBitrate rate in codec_bitrates[payload_type]) { if (rate.timestamp < get_monotonic_time() - 5000000L) { remove.add(rate); continue; } if (rate.bitrate < bitrate) { bitrate = rate.bitrate; } } codec_bitrates[payload_type].remove_all(remove); if (media == "video") { if (bitrate < 128) bitrate = 128; Gst.Caps active_caps = get_active_caps(payload_type); double max_bitrate = get_target_bitrate(device_caps) * 2; double current_target_bitrate = get_target_bitrate(active_caps); int device_caps_width, active_caps_width; device_caps.get_structure(0).get_int("width", out device_caps_width); if (active_caps != null && active_caps.get_size() > 0) { active_caps.get_structure(0).get_int("width", out active_caps_width); } else { active_caps_width = device_caps_width; } if (bitrate < 0.75 * current_target_bitrate && active_caps_width > common_widths[0]) { // Lower video resolution int i = 1; for(; i < common_widths.length && common_widths[i] < active_caps_width; i++);if (common_widths[i] != active_caps_width) { debug("Decrease resolution to ensure target bitrate (%u) is in reach (current resolution target bitrate is %f)", bitrate, current_target_bitrate); } apply_width(payload_type, common_widths[i-1], bitrate); } else if (bitrate > 2 * current_target_bitrate && active_caps_width < device_caps_width) { // Higher video resolution int i = 0; for(; i < common_widths.length && common_widths[i] <= active_caps_width; i++); if (common_widths[i] != active_caps_width) { debug("Increase resolution to make use of available bandwidth of target bitrate (%u) (current resolution target bitrate is %f)", bitrate, current_target_bitrate); } if (common_widths[i] > device_caps_width) { // We never scale up, so just stick with what the device gives apply_width(payload_type, device_caps_width, bitrate); } else if (common_widths[i] != active_caps_width) { apply_width(payload_type, common_widths[i], bitrate); } } if (bitrate > max_bitrate) bitrate = (uint) max_bitrate; } codec_util.update_bitrate(media, payload_type, codecs[payload_type], bitrate); unlock(codec_bitrates); } } public void unlink(Gst.Element? link = null) { if (links <= 0) { critical("Link count below zero."); return; } if (link != null && is_source && tee != null) { PayloadType payload_type = payloader_tees.first_match((entry) => entry.value.any_match((entry) => entry.value == link)).key; uint ssrc = payloader_tees[payload_type].first_match((entry) => entry.value == link).key; payloader_links[payload_type][ssrc] = payloader_links[payload_type][ssrc] - 1; if (payloader_links[payload_type][ssrc] == 0) { plugin.pause(); codec_tees[payload_type].unlink(payloaders[payload_type][ssrc]); payloaders[payload_type][ssrc].set_locked_state(true); payloaders[payload_type][ssrc].set_state(Gst.State.NULL); payloaders[payload_type][ssrc].unlink(payloader_tees[payload_type][ssrc]); pipe.remove(payloaders[payload_type][ssrc]); payloaders[payload_type].unset(ssrc); payloader_tees[payload_type][ssrc].set_locked_state(true); payloader_tees[payload_type][ssrc].set_state(Gst.State.NULL); pipe.remove(payloader_tees[payload_type][ssrc]); payloader_tees[payload_type].unset(ssrc); payloader_links[payload_type].unset(ssrc); plugin.unpause(); } if (payloader_links[payload_type].size == 0) { plugin.pause(); tee.unlink(codecs[payload_type]); codecs[payload_type].set_locked_state(true); codecs[payload_type].set_state(Gst.State.NULL); codecs[payload_type].unlink(codec_tees[payload_type]); pipe.remove(codecs[payload_type]); codecs.unset(payload_type); codec_tees[payload_type].set_locked_state(true); codec_tees[payload_type].set_state(Gst.State.NULL); pipe.remove(codec_tees[payload_type]); codec_tees.unset(payload_type); payloaders.unset(payload_type); payloader_tees.unset(payload_type); payloader_links.unset(payload_type); plugin.unpause(); } } if (link != null && is_sink && mixer != null) { plugin.pause(); link.set_locked_state(true); Gst.Base.AggregatorPad mixer_sink_pad = (Gst.Base.AggregatorPad) link.get_static_pad("src").get_peer(); link.get_static_pad("src").unlink(mixer_sink_pad); mixer_sink_pad.set_active(false); link.set_state(Gst.State.NULL); pipe.remove(link); mixer.release_request_pad(mixer_sink_pad); plugin.unpause(); } links--; if (links == 0) { destroy(); } } private Gst.Caps get_best_caps() { if (media == "audio") { return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1"); } else if (media == "video" && device.caps.get_size() > 0) { int best_index = -1; Value? best_fraction = null; int best_fps = 0; int best_width = 0; int best_height = 0; for (int i = 0; i < device.caps.get_size(); i++) { unowned Gst.Structure? that = device.caps.get_structure(i); Value? best_fraction_now = null; if (!that.has_name("video/x-raw")) continue; int num = 0, den = 0, width = 0, height = 0; if (!that.has_field("framerate")) continue; Value framerate = that.get_value("framerate"); if (framerate.type() == typeof(Gst.Fraction)) { num = Gst.Value.get_fraction_numerator(framerate); den = Gst.Value.get_fraction_denominator(framerate); } else if (framerate.type() == typeof(Gst.ValueList)) { for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { Value fraction = Gst.ValueList.get_value(framerate, j); int in_num = Gst.Value.get_fraction_numerator(fraction); int in_den = Gst.Value.get_fraction_denominator(fraction); int fps = den > 0 ? (num/den) : 0; int in_fps = in_den > 0 ? (in_num/in_den) : 0; if (in_fps > fps) { best_fraction_now = fraction; num = in_num; den = in_den; } } } else { debug("Unknown type for framerate: %s", framerate.type_name()); } if (den == 0) continue; if (!that.has_field("width") || !that.get_int("width", out width)) continue; if (!that.has_field("height") || !that.get_int("height", out height)) continue; int fps = num/den; if (best_fps < fps || best_fps == fps && best_width < width || best_fps == fps && best_width == width && best_height < height) { best_fps = fps; best_width = width; best_height = height; best_index = i; best_fraction = best_fraction_now; } } if (best_index == -1) { // No caps in first round, try without framerate for (int i = 0; i < device.caps.get_size(); i++) { unowned Gst.Structure? that = device.caps.get_structure(i); if (!that.has_name("video/x-raw")) continue; int width = 0, height = 0; if (!that.has_field("width") || !that.get_int("width", out width)) continue; if (!that.has_field("height") || !that.get_int("height", out height)) continue; if (best_width < width || best_width == width && best_height < height) { best_width = width; best_height = height; best_index = i; } } } Gst.Caps res = caps_copy_nth(device.caps, best_index); unowned Gst.Structure? that = res.get_structure(0); Value? framerate = that.get_value("framerate"); if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) { that.set_value("framerate", best_fraction); } debug("Selected caps %s", res.to_string()); return res; } else if (device.caps.get_size() > 0) { return caps_copy_nth(device.caps, 0); } else { return new Gst.Caps.any(); } } // Backport from gst_caps_copy_nth added in GStreamer 1.16 private static Gst.Caps caps_copy_nth(Gst.Caps source, uint index) { Gst.Caps target = new Gst.Caps.empty(); target.flags = source.flags; target.append_structure_full(source.get_structure(index).copy(), source.get_features(index).copy()); return target; } private void create() { debug("Creating device %s", id); plugin.pause(); element = device.create_element(id); if (is_sink) { element.@set("async", false); element.@set("sync", false); } pipe.add(element); device_caps = get_best_caps(); if (is_source) { element.@set("do-timestamp", true); filter = Gst.ElementFactory.make("capsfilter", @"caps_filter_$id"); filter.@set("caps", device_caps); pipe.add(filter); element.link(filter); #if WITH_VOICE_PROCESSOR if (media == "audio" && plugin.echoprobe != null) { dsp = new VoiceProcessor(plugin.echoprobe as EchoProbe, element as Gst.Audio.StreamVolume); dsp.name = @"dsp_$id"; pipe.add(dsp); filter.link(dsp); } #endif tee = Gst.ElementFactory.make("tee", @"tee_$id"); tee.@set("allow-not-linked", true); pipe.add(tee); (dsp ?? filter).link(tee); } if (is_sink && media == "audio") { mixer = (Gst.Base.Aggregator) Gst.ElementFactory.make("audiomixer", @"mixer_$id"); pipe.add(mixer); if (plugin.echoprobe != null && !plugin.echoprobe.get_static_pad("src").is_linked()) { mixer.link(plugin.echoprobe); plugin.echoprobe.link(element); } else { filter = Gst.ElementFactory.make("capsfilter", @"caps_filter_$id"); filter.@set("caps", device_caps); pipe.add(filter); mixer.link(filter); filter.link(element); } } plugin.unpause(); } private void destroy() { if (is_sink) { if (mixer != null) { int linked_sink_pads = 0; mixer.foreach_sink_pad((_, pad) => { if (pad.is_linked()) linked_sink_pads++; return true; }); if (linked_sink_pads > 0) { warning("%s-mixer still has %i sink pads while being destroyed", id, linked_sink_pads); } mixer.unlink(plugin.echoprobe ?? element); } if (filter != null) { filter.set_locked_state(true); filter.set_state(Gst.State.NULL); filter.unlink(element); pipe.remove(filter); filter = null; } if (plugin.echoprobe != null) { plugin.echoprobe.unlink(element); } } element.set_locked_state(true); element.set_state(Gst.State.NULL); if (filter != null) element.unlink(filter); else if (is_source) element.unlink(tee); pipe.remove(element); element = null; if (mixer != null) { mixer.set_locked_state(true); mixer.set_state(Gst.State.NULL); pipe.remove(mixer); mixer = null; } if (is_source) { if (filter != null) { filter.set_locked_state(true); filter.set_state(Gst.State.NULL); filter.unlink(dsp ?? tee); pipe.remove(filter); filter = null; } if (dsp != null) { dsp.set_locked_state(true); dsp.set_state(Gst.State.NULL); dsp.unlink(tee); pipe.remove(dsp); dsp = null; } if (tee != null) { int linked_src_pads = 0; tee.foreach_src_pad((_, pad) => { if (pad.is_linked()) linked_src_pads++; return true; }); if (linked_src_pads != 0) { warning("%s-tee still has %d src pads while being destroyed", id, linked_src_pads); } tee.set_locked_state(true); tee.set_state(Gst.State.NULL); pipe.remove(tee); tee = null; } } debug("Destroyed device %s", id); } } dino-0.4.3/plugins/rtp/src/gst_fixes.c0000644000000000000000000000043414452563620016371 0ustar rootroot#include GstVideoInfo *gst_video_frame_get_video_info(GstVideoFrame *frame) { return &frame->info; } void *gst_video_frame_get_data(GstVideoFrame *frame, size_t* length) { *length = frame->info.height * frame->info.stride[0]; return frame->data[0]; }dino-0.4.3/plugins/rtp/src/module.vala0000644000000000000000000002415414452563620016371 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.Module : JingleRtp.Module { private Set supported_codecs = new HashSet(); private Set unsupported_codecs = new HashSet(); public Plugin plugin { get; private set; } public CodecUtil codec_util { get { return plugin.codec_util; }} public Module(Plugin plugin) { base(); this.plugin = plugin; } private async bool pipeline_works(string media, string element_desc) { var supported = false; string pipeline_desc = @"$(media)testsrc is-live=true ! $element_desc ! appsink name=output"; try { var pipeline = Gst.parse_launch(pipeline_desc); var output = ((Gst.Bin) pipeline).get_by_name("output") as Gst.App.Sink; SourceFunc callback = pipeline_works.callback; var finished = false; output.emit_signals = true; output.new_sample.connect(() => { if (!finished) { finished = true; supported = true; Idle.add(() => { callback(); return Source.REMOVE; }); } return Gst.FlowReturn.EOS; }); pipeline.bus.add_watch(Priority.DEFAULT, (_, message) => { if (message.type == Gst.MessageType.ERROR && !finished) { Error e; string d; message.parse_error(out e, out d); debug("pipeline [%s] failed: %s", pipeline_desc, e.message); debug(d); finished = true; callback(); } return true; }); Timeout.add(2000, () => { if (!finished) { finished = true; callback(); } return Source.REMOVE; }); pipeline.set_state(Gst.State.PLAYING); yield; pipeline.set_state(Gst.State.NULL); } catch (Error e) { debug("pipeline [%s] failed: %s", pipeline_desc, e.message); } return supported; } public override async bool is_payload_supported(string media, JingleRtp.PayloadType payload_type) { string? codec = CodecUtil.get_codec_from_payload(media, payload_type); if (codec == null) return false; if (unsupported_codecs.contains(codec)) return false; if (supported_codecs.contains(codec)) return true; string? encode_element = codec_util.get_encode_element_name(media, codec); string? decode_element = codec_util.get_decode_element_name(media, codec); if (encode_element == null || decode_element == null) { warning("No suitable encoder or decoder found for %s", codec); unsupported_codecs.add(codec); return false; } string encode_bin = codec_util.get_encode_bin_description(media, codec, null, encode_element); while (!(yield pipeline_works(media, encode_bin))) { debug("%s not suited for encoding %s", encode_element, codec); codec_util.mark_element_unsupported(encode_element); encode_element = codec_util.get_encode_element_name(media, codec); if (encode_element == null) { warning("No suitable encoder found for %s", codec); unsupported_codecs.add(codec); return false; } encode_bin = codec_util.get_encode_bin_description(media, codec, null, encode_element); } debug("using %s to encode %s", encode_element, codec); string decode_bin = codec_util.get_decode_bin_description(media, codec, null, decode_element); while (!(yield pipeline_works(media, @"$encode_bin ! $decode_bin"))) { debug("%s not suited for decoding %s", decode_element, codec); codec_util.mark_element_unsupported(decode_element); decode_element = codec_util.get_decode_element_name(media, codec); if (decode_element == null) { warning("No suitable decoder found for %s", codec); unsupported_codecs.add(codec); return false; } decode_bin = codec_util.get_decode_bin_description(media, codec, null, decode_element); } debug("using %s to decode %s", decode_element, codec); supported_codecs.add(codec); return true; } public override bool is_header_extension_supported(string media, JingleRtp.HeaderExtension ext) { if (media == "video" && ext.uri == "urn:3gpp:video-orientation") return true; return false; } public override Gee.List get_suggested_header_extensions(string media) { Gee.List exts = new ArrayList(); if (media == "video") { exts.add(new JingleRtp.HeaderExtension(1, "urn:3gpp:video-orientation")); } return exts; } public async void add_if_supported(Gee.List list, string media, JingleRtp.PayloadType payload_type) { if (yield is_payload_supported(media, payload_type)) { list.add(payload_type); } } public override async Gee.List get_supported_payloads(string media) { Gee.List list = new ArrayList(JingleRtp.PayloadType.equals_func); if (media == "audio") { var opus = new JingleRtp.PayloadType() { channels = 1, clockrate = 48000, name = "opus", id = 111, channels = 2 }; opus.parameters["useinbandfec"] = "1"; var speex32 = new JingleRtp.PayloadType() { channels = 1, clockrate = 32000, name = "speex", id = 112 }; var speex16 = new JingleRtp.PayloadType() { channels = 1, clockrate = 16000, name = "speex", id = 113 }; var speex8 = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "speex", id = 114 }; var g722 = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "G722", id = 9 }; var pcmu = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "PCMU", id = 0 }; var pcma = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "PCMA", id = 8 }; yield add_if_supported(list, media, opus); yield add_if_supported(list, media, speex32); yield add_if_supported(list, media, speex16); yield add_if_supported(list, media, speex8); yield add_if_supported(list, media, g722); yield add_if_supported(list, media, pcmu); yield add_if_supported(list, media, pcma); } else if (media == "video") { var rtcp_fbs = new ArrayList(); rtcp_fbs.add(new JingleRtp.RtcpFeedback("goog-remb")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("ccm", "fir")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("nack")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("nack", "pli")); #if ENABLE_H264 var h264 = new JingleRtp.PayloadType() { clockrate = 90000, name = "H264", id = 96 }; yield add_if_supported(list, media, h264); h264.rtcp_fbs.add_all(rtcp_fbs); #endif #if ENABLE_VP9 var vp9 = new JingleRtp.PayloadType() { clockrate = 90000, name = "VP9", id = 97 }; vp9.rtcp_fbs.add_all(rtcp_fbs); yield add_if_supported(list, media, vp9); #endif var vp8 = new JingleRtp.PayloadType() { clockrate = 90000, name = "VP8", id = 98 }; vp8.rtcp_fbs.add_all(rtcp_fbs); yield add_if_supported(list, media, vp8); } else { warning("Unsupported media type: %s", media); } return list; } public override async JingleRtp.PayloadType? pick_payload_type(string media, Gee.List payloads) { if (media == "audio" || media == "video") { foreach (JingleRtp.PayloadType type in payloads) { if (yield is_payload_supported(media, type)) return adjust_payload_type(media, type.clone()); } } else { warning("Unsupported media type: %s", media); } return null; } public JingleRtp.PayloadType adjust_payload_type(string media, JingleRtp.PayloadType type) { var iter = type.rtcp_fbs.iterator(); while (iter.next()) { var fb = iter.@get(); switch (fb.type_) { case "goog-remb": if (fb.subtype != null) iter.remove(); break; case "ccm": if (fb.subtype != "fir") iter.remove(); break; case "nack": if (fb.subtype != null && fb.subtype != "pli") iter.remove(); break; default: iter.remove(); break; } } return type; } public override JingleRtp.Stream create_stream(Jingle.Content content) { return plugin.open_stream(content); } public override void close_stream(JingleRtp.Stream stream) { var rtp_stream = stream as Rtp.Stream; plugin.close_stream(rtp_stream); } public override JingleRtp.Crypto? generate_local_crypto() { uint8[] key_and_salt = new uint8[30]; Crypto.randomize(key_and_salt); return JingleRtp.Crypto.create(JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_80, key_and_salt); } public override JingleRtp.Crypto? pick_remote_crypto(Gee.List cryptos) { foreach (JingleRtp.Crypto crypto in cryptos) { if (crypto.is_valid) return crypto; } return null; } public override JingleRtp.Crypto? pick_local_crypto(JingleRtp.Crypto? remote) { if (remote == null || !remote.is_valid) return null; uint8[] key_and_salt = new uint8[30]; Crypto.randomize(key_and_salt); return remote.rekey(key_and_salt); } } dino-0.4.3/plugins/rtp/src/participant.vala0000644000000000000000000000204614452563620017416 0ustar rootrootusing Gee; using Xmpp; public class Dino.Plugins.Rtp.Participant { public Jid full_jid { get; private set; } protected Gst.Pipeline pipe; private Map ssrcs = new HashMap(); public Participant(Gst.Pipeline pipe, Jid full_jid) { this.pipe = pipe; this.full_jid = full_jid; } public uint32 get_ssrc(Stream stream) { if (ssrcs.has_key(stream)) { return ssrcs[stream]; } return 0; } public void set_ssrc(Stream stream, uint32 ssrc) { if (ssrcs.has_key(stream)) { warning("Learning ssrc %ul for %s in %s when it is already known as %ul", ssrc, full_jid.to_string(), stream.to_string(), ssrcs[stream]); } else { stream.on_destroy.connect(unset_ssrc); } ssrcs[stream] = ssrc; } public void unset_ssrc(Stream stream) { ssrcs.unset(stream); stream.on_destroy.disconnect(unset_ssrc); } public string to_string() { return @"participant $full_jid"; } }dino-0.4.3/plugins/rtp/src/plugin.vala0000644000000000000000000004406414452563620016404 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { public Dino.Application app { get; private set; } public CodecUtil codec_util { get; private set; } public Gst.DeviceMonitor? device_monitor { get; private set; } public Gst.Pipeline? pipe { get; private set; } public Gst.Bin? rtpbin { get; private set; } public Gst.Element? echoprobe { get; private set; } private Gee.List streams = new ArrayList(); private Gee.List devices = new ArrayList(); // private Gee.List participants = new ArrayList(); public void registered(Dino.Application app) { this.app = app; this.codec_util = new CodecUtil(); app.startup.connect(startup); app.add_option_group(Gst.init_get_option_group()); app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { list.add(new Module(this)); }); app.plugin_registry.video_call_plugin = this; } private int pause_count = 0; public void pause() { // if (pause_count == 0) { // debug("Pausing pipe for modifications"); // pipe.set_state(Gst.State.PAUSED); // } pause_count++; } public void unpause() { pause_count--; if (pause_count == 0) { debug("Continue pipe after modifications"); pipe.set_state(Gst.State.PLAYING); } if (pause_count < 0) warning("Pause count below zero!"); } private void init_device_monitor() { if (device_monitor != null) return; device_monitor = new Gst.DeviceMonitor(); device_monitor.show_all = true; device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message); device_monitor.start(); foreach (Gst.Device device in device_monitor.get_devices()) { if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue; if (device.properties.get_string("device.class") == "monitor") continue; if (devices.any_match((it) => it.matches(device))) continue; devices.add(new Device(this, device)); } } private void init_call_pipe() { if (pipe != null) return; pipe = new Gst.Pipeline(null); // RTP rtpbin = Gst.ElementFactory.make("rtpbin", null) as Gst.Bin; if (rtpbin == null) { warning("RTP not supported"); pipe = null; return; } rtpbin.pad_added.connect(on_rtp_pad_added); rtpbin.@set("latency", 100); rtpbin.@set("do-lost", true); // rtpbin.@set("do-sync-event", true); rtpbin.@set("drop-on-latency", true); rtpbin.connect("signal::request-pt-map", request_pt_map, this); pipe.add(rtpbin); #if WITH_VOICE_PROCESSOR // Audio echo probe echoprobe = new EchoProbe(); if (echoprobe != null) pipe.add(echoprobe); #endif // Pipeline pipe.auto_flush_bus = true; pipe.bus.add_watch(GLib.Priority.DEFAULT, (_, message) => { on_pipe_bus_message(message); return true; }); pipe.set_state(Gst.State.PLAYING); } private void destroy_call_pipe() { if (pipe == null) return; pipe.set_state(Gst.State.NULL); rtpbin = null; #if WITH_VOICE_PROCESSOR echoprobe = null; #endif pipe = null; } public void startup() { init_device_monitor(); } private static Gst.Caps? request_pt_map(Gst.Element rtpbin, uint session, uint pt, Plugin plugin) { debug("request-pt-map"); return null; } private void on_rtp_pad_added(Gst.Pad pad) { debug("pad added: %s", pad.name); if (pad.name.has_prefix("recv_rtp_src_")) { string[] split = pad.name.split("_"); uint8 rtpid = (uint8)int.parse(split[3]); foreach (Stream stream in streams) { if (stream.rtpid == rtpid) { stream.on_ssrc_pad_added((uint32) split[4].to_uint64(), pad); } } } if (pad.name.has_prefix("send_rtp_src_")) { string[] split = pad.name.split("_"); uint8 rtpid = (uint8)int.parse(split[3]); debug("pad %s for stream %hhu", pad.name, rtpid); foreach (Stream stream in streams) { if (stream.rtpid == rtpid) { stream.on_send_rtp_src_added(pad); } } } } private void on_pipe_bus_message(Gst.Message message) { switch (message.type) { case Gst.MessageType.ERROR: Error error; string str; message.parse_error(out error, out str); warning("Error in pipeline: %s", error.message); debug(str); break; case Gst.MessageType.WARNING: Error error; string str; message.parse_warning(out error, out str); warning("Warning in pipeline: %s", error.message); debug(str); break; case Gst.MessageType.CLOCK_LOST: debug("Clock lost. Restarting"); pipe.set_state(Gst.State.READY); pipe.set_state(Gst.State.PLAYING); break; case Gst.MessageType.STATE_CHANGED: // Ignore break; case Gst.MessageType.STREAM_STATUS: Gst.StreamStatusType status; Gst.Element owner; message.parse_stream_status(out status, out owner); if (owner != null) { debug("%s stream changed status to %s", owner.name, status.to_string()); } break; case Gst.MessageType.ELEMENT: unowned Gst.Structure struc = message.get_structure(); if (struc != null && message.src is Gst.Element) { debug("Message from %s in pipeline: %s", ((Gst.Element)message.src).name, struc.to_string()); } break; case Gst.MessageType.NEW_CLOCK: debug("New clock."); break; case Gst.MessageType.TAG: // Ignore break; case Gst.MessageType.QOS: // Ignore break; case Gst.MessageType.LATENCY: if (message.src != null && message.src.name != null && message.src is Gst.Element) { Gst.Query latency_query = new Gst.Query.latency(); if (((Gst.Element)message.src).query(latency_query)) { bool live; Gst.ClockTime min_latency, max_latency; latency_query.parse_latency(out live, out min_latency, out max_latency); debug("Latency message from %s: live=%s, min_latency=%s, max_latency=%s", message.src.name, live.to_string(), min_latency.to_string(), max_latency.to_string()); } } break; default: debug("Pipe bus message: %s", message.type.to_string()); break; } } private bool on_device_monitor_message(Gst.Bus bus, Gst.Message message) { Gst.Device? old_gst_device = null; Gst.Device? gst_device = null; Device? device = null; switch (message.type) { case Gst.MessageType.DEVICE_ADDED: message.parse_device_added(out gst_device); if (devices.any_match((it) => it.matches(gst_device))) return Source.CONTINUE; device = new Device(this, gst_device); devices.add(device); break; #if GST_1_16 case Gst.MessageType.DEVICE_CHANGED: message.parse_device_changed(out gst_device, out old_gst_device); device = devices.first_match((it) => it.matches(old_gst_device)); if (device != null) device.update(gst_device); break; #endif case Gst.MessageType.DEVICE_REMOVED: message.parse_device_removed(out gst_device); device = devices.first_match((it) => it.matches(gst_device)); if (device != null) devices.remove(device); break; default: break; } if (device != null) { devices_changed(device.media, device.is_sink); } return Source.CONTINUE; } public uint8 next_free_id() { uint8 rtpid = 0; while (streams.size < 100 && streams.any_match((stream) => stream.rtpid == rtpid)) { rtpid++; } return rtpid; } // public Participant get_participant(Jid full_jid, bool self) { // foreach (Participant participant in participants) { // if (participant.full_jid.equals(full_jid)) { // return participant; // } // } // Participant participant; // if (self) { // participant = new SelfParticipant(pipe, full_jid); // } else { // participant = new Participant(pipe, full_jid); // } // participants.add(participant); // return participant; // } public Stream open_stream(Xmpp.Xep.Jingle.Content content) { init_call_pipe(); var content_params = content.content_params as Xmpp.Xep.JingleRtp.Parameters; if (content_params == null) return null; Stream stream; if (content_params.media == "video") { stream = new VideoStream(this, content); } else { stream = new Stream(this, content); } streams.add(stream); return stream; } public void close_stream(Stream stream) { streams.remove(stream); stream.destroy(); } public void shutdown() { if (device_monitor != null) { device_monitor.stop(); } destroy_call_pipe(); Gst.deinit(); } public bool supports(string? media) { if (!codec_util.is_element_supported("rtpbin")) return false; if (media == "audio") { if (get_devices("audio", false).is_empty) return false; if (get_devices("audio", true).is_empty) return false; } if (media == "video") { if (!codec_util.is_element_supported("gtksink")) return false; if (get_devices("video", false).is_empty) return false; } return true; } public VideoCallWidget? create_widget(WidgetType type) { init_call_pipe(); if (type == WidgetType.GTK4) { return new VideoWidget(this); } return null; } public Gee.List get_devices(string media, bool incoming) { Gee.List devices; if (media == "video" && !incoming) { devices = get_video_sources(); } else if (media == "audio") { devices = get_audio_devices(incoming); } else { devices = new ArrayList(); devices.add_all_iterator(this.devices.filter(it => it.media == media && (incoming && it.is_sink || !incoming && it.is_source) && !it.is_monitor)); } devices.sort((media_left, media_right) => { return strcmp(media_left.id, media_right.id); }); return devices; } public Gee.List get_audio_devices(bool incoming) { ArrayList pulse_devices = new ArrayList(); ArrayList other_devices = new ArrayList(); foreach (Device device in devices) { if (device.media != "audio") continue; if (incoming && !device.is_sink || !incoming && !device.is_source) continue; // Skip monitors if (device.is_monitor) continue; if (device.protocol == DeviceProtocol.PULSEAUDIO) { pulse_devices.add(device); } else { other_devices.add(device); } } // If we have any pulseaudio devices, present only those. Don't want duplicated devices from pipewire and pulseaudio. return pulse_devices.size > 0 ? pulse_devices : other_devices; } public Gee.List get_video_sources() { ArrayList pipewire_devices = new ArrayList(); ArrayList other_devices = new ArrayList(); foreach (Device device in devices) { if (device.media != "video") continue; if (device.is_sink) continue; // Skip monitors if (device.is_monitor) continue; bool is_color = false; for (int i = 0; i < device.device.caps.get_size(); i++) { unowned Gst.Structure structure = device.device.caps.get_structure(i); if (!structure.has_field("format")) continue; // "format" might be an array and get_string() will then return null. We just assume arrays to be fine. string? format = structure.get_string("format"); if (format == null || !format.has_prefix("GRAY")) { is_color = true; } } // Don't allow grey-scale devices if (!is_color) continue; if (device.protocol == DeviceProtocol.PIPEWIRE) { pipewire_devices.add(device); } else { other_devices.add(device); } } // If we have any pipewire devices, present only those. Don't want duplicated devices from pipewire and video for linux. return pipewire_devices.size > 0 ? pipewire_devices : other_devices; } private int get_max_fps(Device device) { int fps = 0; for (int i = 0; i < device.device.caps.get_size(); i++) { unowned Gst.Structure structure = device.device.caps.get_structure(i); if (structure.has_field("framerate")) { Value framerate = structure.get_value("framerate"); if (framerate.type() == typeof(Gst.Fraction)) { int num = Gst.Value.get_fraction_numerator(framerate); int den = Gst.Value.get_fraction_denominator(framerate); fps = int.max(fps, num / den); } else if (framerate.type() == typeof(Gst.ValueList)) { for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { Value fraction = Gst.ValueList.get_value(framerate, j); int num = Gst.Value.get_fraction_numerator(fraction); int den = Gst.Value.get_fraction_denominator(fraction); fps = int.max(fps, num / den); } } else { debug("Unknown type for framerate %s on device %s", framerate.type_name(), device.display_name); } } } debug("Max framerate for device %s: %d", device.display_name, fps); return fps; } public MediaDevice? get_preferred_device(string media, bool incoming) { Gee.List devices = new ArrayList(); foreach (MediaDevice media_device in get_devices(media, incoming)) { if (media_device is Device) devices.add((Device)media_device); } if (devices.is_empty) { warning("No preferred device for %s %s. Media will not be processed.", incoming ? "incoming" : "outgoing", media); return null; } // Take default if present foreach (Device device in devices) { if (device.is_default) { debug("Using %s for %s %s as it's default", device.display_name, incoming ? "incoming" : "outgoing", media); return device; } } if (media == "video") { // Pick best FPS int max_fps = -1; Device? max_fps_device = null; foreach (Device device in devices) { int fps = get_max_fps(device); if (fps > max_fps || max_fps_device == null) { max_fps = fps; max_fps_device = device; } } debug("Using %s for %s %s as it has max FPS (%d)", max_fps_device.display_name, incoming ? "incoming" : "outgoing", media, max_fps); return max_fps_device; } else { // Pick any Device? device = devices.first(); debug("Using %s for %s %s as it's first pick", device.display_name, incoming ? "incoming" : "outgoing", media); return device; } } public MediaDevice? get_device(Xmpp.Xep.JingleRtp.Stream? stream, bool incoming) { Stream? plugin_stream = stream as Stream?; if (plugin_stream == null) return null; MediaDevice? device = incoming ? plugin_stream.output_device : plugin_stream.input_device; return device ?? get_preferred_device(stream.media, incoming); } public void dump_dot() { if (pipe == null) return; string name = @"pipe-$(pipe.clock.get_time())-$(pipe.current_state)"; Gst.Debug.bin_to_dot_file(pipe, Gst.DebugGraphDetails.ALL, name); print(@"Stored pipe details as $name\n"); } public void set_pause(Xmpp.Xep.JingleRtp.Stream? stream, bool pause) { Stream? plugin_stream = stream as Stream?; if (plugin_stream == null) return; if (pause) { plugin_stream.pause(); } else { plugin_stream.unpause(); } } public void set_device(Xmpp.Xep.JingleRtp.Stream? stream, MediaDevice? device) { Device? real_device = device as Device?; Stream? plugin_stream = stream as Stream?; if (real_device == null || plugin_stream == null) return; if (real_device.is_source) { plugin_stream.input_device = real_device; } else if (real_device.is_sink) { plugin_stream.output_device = real_device; } } } dino-0.4.3/plugins/rtp/src/register_plugin.vala0000644000000000000000000000013414452563620020276 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Rtp.Plugin); } dino-0.4.3/plugins/rtp/src/stream.vala0000644000000000000000000010320514452563620016372 0ustar rootrootusing Gee; using Xmpp; public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { public uint8 rtpid { get; private set; } public Plugin plugin { get; private set; } public Gst.Pipeline pipe { get { return plugin.pipe; }} public Gst.Element rtpbin { get { return plugin.rtpbin; }} public CodecUtil codec_util { get { return plugin.codec_util; }} private Gst.App.Sink send_rtp; private Gst.App.Sink send_rtcp; private Gst.App.Src recv_rtp; private Gst.App.Src recv_rtcp; private Gst.Element decode; private Gst.RTP.BaseDepayload decode_depay; private Gst.Element input; private Gst.Pad input_pad; private Gst.Element output; private Gst.Element session; private Device _input_device; public Device input_device { get { return _input_device; } set { if (sending && !paused) { var input = this.input; set_input(value != null ? value.link_source(payload_type, our_ssrc, next_seqnum_offset, next_timestamp_offset) : null); if (this._input_device != null) this._input_device.unlink(input); } this._input_device = value; }} private Device _output_device; public Device output_device { get { return _output_device; } set { if (output != null) remove_output(output); if (value != null && receiving) add_output(value.link_sink()); this._output_device = value; }} public bool created { get; private set; default = false; } public bool paused { get; private set; default = false; } private bool push_recv_data = false; private uint our_ssrc = Random.next_int(); private int next_seqnum_offset = -1; private uint32 next_timestamp_offset_base = 0; private int64 next_timestamp_offset_stamp = 0; private uint32 next_timestamp_offset { get { if (next_timestamp_offset_base == 0) return 0; int64 monotonic_diff = get_monotonic_time() - next_timestamp_offset_stamp; return next_timestamp_offset_base + (uint32)((double)monotonic_diff / 1000000.0 * payload_type.clockrate); } } private uint32 participant_ssrc = 0; private Gst.Pad recv_rtcp_sink_pad; private Gst.Pad recv_rtp_sink_pad; private Gst.Pad recv_rtp_src_pad; private Gst.Pad send_rtcp_src_pad; private Gst.Pad send_rtp_sink_pad; private Gst.Pad send_rtp_src_pad; private Crypto.Srtp.Session? crypto_session = new Crypto.Srtp.Session(); public Stream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(content); this.plugin = plugin; this.rtpid = plugin.next_free_id(); content.notify["senders"].connect_after(on_senders_changed); } public void on_senders_changed() { if (sending && input == null) { input_device = input_device; } if (receiving && output == null) { output_device = output_device; } } public override void create() { plugin.pause(); // Create i/o if needed if (input == null && sending) { input_device = input_device; } if (output == null && receiving && media == "audio") { output_device = output_device; } // Create app elements send_rtp = Gst.ElementFactory.make("appsink", @"rtp_sink_$rtpid") as Gst.App.Sink; send_rtp.async = false; send_rtp.caps = CodecUtil.get_caps(media, payload_type, false); send_rtp.emit_signals = true; send_rtp.sync = true; send_rtp.drop = true; send_rtp.wait_on_eos = false; send_rtp.new_sample.connect(on_new_sample); #if GST_1_20 send_rtp.new_serialized_event.connect(on_new_event); #endif send_rtp.connect("signal::eos", on_eos_static, this); pipe.add(send_rtp); send_rtcp = Gst.ElementFactory.make("appsink", @"rtcp_sink_$rtpid") as Gst.App.Sink; send_rtcp.async = false; send_rtcp.caps = new Gst.Caps.empty_simple("application/x-rtcp"); send_rtcp.emit_signals = true; send_rtcp.sync = true; send_rtcp.drop = true; send_rtcp.wait_on_eos = false; send_rtcp.new_sample.connect(on_new_sample); send_rtcp.connect("signal::eos", on_eos_static, this); pipe.add(send_rtcp); recv_rtp = Gst.ElementFactory.make("appsrc", @"rtp_src_$rtpid") as Gst.App.Src; recv_rtp.caps = CodecUtil.get_caps(media, payload_type, true); recv_rtp.do_timestamp = true; recv_rtp.format = Gst.Format.TIME; recv_rtp.is_live = true; pipe.add(recv_rtp); recv_rtcp = Gst.ElementFactory.make("appsrc", @"rtcp_src_$rtpid") as Gst.App.Src; recv_rtcp.caps = new Gst.Caps.empty_simple("application/x-rtcp"); recv_rtcp.do_timestamp = true; recv_rtcp.format = Gst.Format.TIME; recv_rtcp.is_live = true; pipe.add(recv_rtcp); // Connect RTCP send_rtcp_src_pad = rtpbin.get_request_pad(@"send_rtcp_src_$rtpid"); send_rtcp_src_pad.link(send_rtcp.get_static_pad("sink")); recv_rtcp_sink_pad = rtpbin.get_request_pad(@"recv_rtcp_sink_$rtpid"); recv_rtcp.get_static_pad("src").link(recv_rtcp_sink_pad); // Connect input send_rtp_sink_pad = rtpbin.get_request_pad(@"send_rtp_sink_$rtpid"); if (input != null) { input_pad = input.get_request_pad(@"src_$rtpid"); input_pad.link(send_rtp_sink_pad); } // Connect output decode = codec_util.get_decode_bin(media, payload_type, @"decode_$rtpid"); decode_depay = (Gst.RTP.BaseDepayload)((Gst.Bin)decode).get_by_name(@"decode_$(rtpid)_rtp_depay"); pipe.add(decode); if (output != null) { decode.link(output); } // Connect RTP recv_rtp_sink_pad = rtpbin.get_request_pad(@"recv_rtp_sink_$rtpid"); recv_rtp.get_static_pad("src").link(recv_rtp_sink_pad); created = true; push_recv_data = true; plugin.unpause(); GLib.Signal.emit_by_name(rtpbin, "get-session", rtpid, out session); if (session != null && remb_enabled) { Object internal_session; session.@get("internal-session", out internal_session); if (internal_session != null) { internal_session.connect("signal::on-feedback-rtcp", on_feedback_rtcp, this); } Timeout.add(1000, () => remb_adjust()); } if (input_device != null && media == "video") { input_device.update_bitrate(payload_type, target_send_bitrate); } } private int last_packets_lost = -1; private uint64 last_packets_received = 0; private uint64 last_octets_received = 0; private uint max_target_receive_bitrate = 0; private int64 last_remb_time = 0; private bool remb_adjust() { unowned Gst.Structure? stats; if (session == null) { debug("Session for %u finished, turning off remb adjustment", rtpid); return Source.REMOVE; } session.get("stats", out stats); if (stats == null) { warning("No stats for session %u", rtpid); return Source.REMOVE; } unowned ValueArray? source_stats; stats.get("source-stats", typeof(ValueArray), out source_stats); if (source_stats == null) { warning("No source-stats for session %u", rtpid); return Source.REMOVE; } if (input_device == null) return Source.CONTINUE; foreach (Value value in source_stats.values) { unowned Gst.Structure source_stat = (Gst.Structure) value.get_boxed(); uint32 ssrc; if (!source_stat.get_uint("ssrc", out ssrc)) continue; if (ssrc == participant_ssrc) { int packets_lost; uint64 packets_received, octets_received; source_stat.get_int("packets-lost", out packets_lost); source_stat.get_uint64("packets-received", out packets_received); source_stat.get_uint64("octets-received", out octets_received); int new_lost = packets_lost - last_packets_lost; if (new_lost < 0) new_lost = 0; uint64 new_received = packets_received - last_packets_received; if (packets_received < last_packets_received) new_received = 0; uint64 new_octets = octets_received - last_octets_received; if (octets_received < last_octets_received) octets_received = 0; if (new_received == 0) continue; last_packets_lost = packets_lost; last_packets_received = packets_received; last_octets_received = octets_received; double loss_rate = (double)new_lost / (double)(new_lost + new_received); uint new_target_receive_bitrate; if (new_lost <= 0 || loss_rate < 0.02) { new_target_receive_bitrate = (uint)(1.08 * (double)target_receive_bitrate); } else if (loss_rate > 0.1) { new_target_receive_bitrate = (uint)((1.0 - 0.5 * loss_rate) * (double)target_receive_bitrate); } else { new_target_receive_bitrate = target_receive_bitrate; } if (last_remb_time == 0) { last_remb_time = get_monotonic_time(); } else { int64 time_now = get_monotonic_time(); int64 time_diff = time_now - last_remb_time; last_remb_time = time_now; uint actual_bitrate = (uint)(((double)new_octets * 8.0) * (double)time_diff / 1000.0 / 1000000.0); new_target_receive_bitrate = uint.max(new_target_receive_bitrate, (uint)(0.9 * (double)actual_bitrate)); max_target_receive_bitrate = uint.max((uint)(1.5 * (double)actual_bitrate), max_target_receive_bitrate); new_target_receive_bitrate = uint.min(new_target_receive_bitrate, max_target_receive_bitrate); } new_target_receive_bitrate = uint.max(16, new_target_receive_bitrate); // Never go below 16 if (new_target_receive_bitrate != target_receive_bitrate) { target_receive_bitrate = new_target_receive_bitrate; uint8[] data = new uint8[] { 143, 206, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'R', 'E', 'M', 'B', 1, 0, 0, 0, 0, 0, 0, 0 }; data[4] = (uint8)((our_ssrc >> 24) & 0xff); data[5] = (uint8)((our_ssrc >> 16) & 0xff); data[6] = (uint8)((our_ssrc >> 8) & 0xff); data[7] = (uint8)(our_ssrc & 0xff); uint8 br_exp = 0; uint32 br_mant = target_receive_bitrate * 1000; uint8 bits = (uint8)Math.log2(br_mant); if (bits > 16) { br_exp = (uint8)bits - 16; br_mant = br_mant >> br_exp; } data[17] = (uint8)((br_exp << 2) | ((br_mant >> 16) & 0x3)); data[18] = (uint8)((br_mant >> 8) & 0xff); data[19] = (uint8)(br_mant & 0xff); data[20] = (uint8)((ssrc >> 24) & 0xff); data[21] = (uint8)((ssrc >> 16) & 0xff); data[22] = (uint8)((ssrc >> 8) & 0xff); data[23] = (uint8)(ssrc & 0xff); encrypt_and_send_rtcp(data); } } } return Source.CONTINUE; } private static void on_feedback_rtcp(Gst.Element session, uint type, uint fbtype, uint sender_ssrc, uint media_ssrc, Gst.Buffer? fci, Stream self) { if (self.input_device != null && self.media == "video" && type == 206 && fbtype == 15 && fci != null && sender_ssrc == self.participant_ssrc) { // https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 uint8[] data; fci.extract_dup(0, fci.get_size(), out data); if (data[0] != 'R' || data[1] != 'E' || data[2] != 'M' || data[3] != 'B') return; uint8 br_exp = data[5] >> 2; uint32 br_mant = (((uint32)data[5] & 0x3) << 16) + ((uint32)data[6] << 8) + (uint32)data[7]; self.target_send_bitrate = (br_mant << br_exp) / 1000; self.input_device.update_bitrate(self.payload_type, self.target_send_bitrate); } } private void prepare_local_crypto() { if (local_crypto != null && local_crypto.is_valid && !crypto_session.has_encrypt) { crypto_session.set_encryption_key(local_crypto.crypto_suite, local_crypto.key, local_crypto.salt); debug("Setting up encryption with key params %s", local_crypto.key_params); } } bool flip = false; uint8 rotation = 0; #if GST_1_20 private bool on_new_event(Gst.App.Sink sink) { if (sink == null || sink != send_rtp) { return false; } Gst.MiniObject obj = sink.try_pull_object(0); if (obj.type == typeof(Gst.Event)) { unowned Gst.TagList tags; if (((Gst.Event)obj).type == Gst.EventType.TAG) { ((Gst.Event)obj).parse_tag(out tags); Gst.Video.OrientationMethod orientation_method; Gst.Video.Orientation.from_tag(tags, out orientation_method); switch (orientation_method) { case Gst.Video.OrientationMethod.IDENTITY: case Gst.Video.OrientationMethod.VERT: default: rotation = 0; break; case Gst.Video.OrientationMethod.@90R: case Gst.Video.OrientationMethod.UL_LR: rotation = 1; break; case Gst.Video.OrientationMethod.@180: case Gst.Video.OrientationMethod.HORIZ: rotation = 2; break; case Gst.Video.OrientationMethod.@90L: case Gst.Video.OrientationMethod.UR_LL: rotation = 3; break; } switch (orientation_method) { case Gst.Video.OrientationMethod.IDENTITY: case Gst.Video.OrientationMethod.@90R: case Gst.Video.OrientationMethod.@180: case Gst.Video.OrientationMethod.@90L: default: flip = false; break; case Gst.Video.OrientationMethod.VERT: case Gst.Video.OrientationMethod.UL_LR: case Gst.Video.OrientationMethod.HORIZ: case Gst.Video.OrientationMethod.UR_LL: flip = true; break; } } } return false; } #endif private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) { if (sink == null) { debug("Sink is null"); return Gst.FlowReturn.EOS; } if (sink != send_rtp && sink != send_rtcp) { warning("unknown sample"); return Gst.FlowReturn.NOT_SUPPORTED; } Gst.Sample sample = sink.pull_sample(); Gst.Buffer buffer = sample.get_buffer(); if (sink == send_rtp) { uint buffer_ssrc = 0, buffer_seq = 0; Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { buffer_ssrc = rtp_buffer.get_ssrc(); buffer_seq = rtp_buffer.get_seq(); next_seqnum_offset = rtp_buffer.get_seq() + 1; next_timestamp_offset_base = rtp_buffer.get_timestamp(); next_timestamp_offset_stamp = get_monotonic_time(); rtp_buffer.unmap(); } #if GLIB_2_64 if (our_ssrc != buffer_ssrc) { warning_once("Sending RTP %s buffer seq %u with SSRC %u when our ssrc is %u", media, buffer_seq, buffer_ssrc, our_ssrc); } #endif } #if GST_1_20 if (sink == send_rtp) { Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation"); if (ext != null) { buffer = (Gst.Buffer) buffer.make_writable(); Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.WRITE, out rtp_buffer)) { uint8[] extension_data = new uint8[1]; bool camera = false; extension_data[0] = extension_data[0] | (rotation & 0x3); if (flip) extension_data[0] = extension_data[0] | 0x4; if (camera) extension_data[0] = extension_data[0] | 0x8; rtp_buffer.add_extension_onebyte_header(ext.id, extension_data); } } } #endif prepare_local_crypto(); uint8[] data; buffer.extract_dup(0, buffer.get_size(), out data); if (sink == send_rtp) { encrypt_and_send_rtp((owned) data); } else if (sink == send_rtcp) { encrypt_and_send_rtcp((owned) data); } return Gst.FlowReturn.OK; } private void encrypt_and_send_rtp(owned uint8[] data) { Bytes bytes; if (crypto_session.has_encrypt) { bytes = new Bytes.take(crypto_session.encrypt_rtp(data)); } else { bytes = new Bytes.take(data); } on_send_rtp_data(bytes); } private void encrypt_and_send_rtcp(owned uint8[] data) { Bytes bytes; if (crypto_session.has_encrypt) { bytes = new Bytes.take(crypto_session.encrypt_rtcp(data)); } else { bytes = new Bytes.take(data); } if (rtcp_mux) { on_send_rtp_data(bytes); } else { on_send_rtcp_data(bytes); } } private static Gst.PadProbeReturn drop_probe() { return Gst.PadProbeReturn.DROP; } private static void on_eos_static(Gst.App.Sink sink, Stream self) { debug("EOS on %s", sink.name); if (sink == self.send_rtp) { Idle.add(() => { self.on_send_rtp_eos(); return Source.REMOVE; }); } else if (sink == self.send_rtcp) { Idle.add(() => { self.on_send_rtcp_eos(); return Source.REMOVE; }); } } private void on_send_rtp_eos() { if (send_rtp_src_pad != null) { send_rtp_src_pad.unlink(send_rtp.get_static_pad("sink")); send_rtp_src_pad = null; } send_rtp.set_locked_state(true); send_rtp.set_state(Gst.State.NULL); pipe.remove(send_rtp); send_rtp = null; debug("Stopped sending RTP for %u", rtpid); } private void on_send_rtcp_eos() { send_rtcp.set_locked_state(true); send_rtcp.set_state(Gst.State.NULL); pipe.remove(send_rtcp); send_rtcp = null; debug("Stopped sending RTCP for %u", rtpid); } public override void destroy() { // Stop network communication push_recv_data = false; if (recv_rtp != null) recv_rtp.end_of_stream(); if (recv_rtcp != null) recv_rtcp.end_of_stream(); if (send_rtp != null) send_rtp.new_sample.disconnect(on_new_sample); if (send_rtcp != null) send_rtcp.new_sample.disconnect(on_new_sample); // Disconnect input device if (input != null) { input_pad.unlink(send_rtp_sink_pad); input.release_request_pad(input_pad); input_pad = null; } if (this._input_device != null) { if (!paused) this._input_device.unlink(input); this._input_device = null; this.input = null; } // Inject EOS if (send_rtp_sink_pad != null) { send_rtp_sink_pad.send_event(new Gst.Event.eos()); } // Disconnect decode if (recv_rtp_src_pad != null) { recv_rtp_src_pad.add_probe(Gst.PadProbeType.BLOCK, drop_probe); recv_rtp_src_pad.unlink(decode.get_static_pad("sink")); } // Disconnect output if (output != null) { decode.get_static_pad("src").add_probe(Gst.PadProbeType.BLOCK, drop_probe); decode.unlink(output); } // Disconnect output device if (this._output_device != null) { this._output_device.unlink(output); this._output_device = null; } output = null; // Destroy decode if (decode != null) { decode.set_locked_state(true); decode.set_state(Gst.State.NULL); pipe.remove(decode); decode = null; decode_depay = null; } // Disconnect and remove RTP input if (recv_rtp != null) { recv_rtp.get_static_pad("src").unlink(recv_rtp_sink_pad); recv_rtp.set_locked_state(true); recv_rtp.set_state(Gst.State.NULL); pipe.remove(recv_rtp); recv_rtp = null; } // Disconnect and remove RTCP input if (recv_rtcp != null) { recv_rtcp.get_static_pad("src").unlink(recv_rtcp_sink_pad); recv_rtcp.set_locked_state(true); recv_rtcp.set_state(Gst.State.NULL); pipe.remove(recv_rtcp); recv_rtcp = null; } // Release rtp pads if (send_rtp_sink_pad != null) { rtpbin.release_request_pad(send_rtp_sink_pad); send_rtp_sink_pad = null; } if (recv_rtp_sink_pad != null) { rtpbin.release_request_pad(recv_rtp_sink_pad); recv_rtp_sink_pad = null; } if (send_rtcp_src_pad != null) { rtpbin.release_request_pad(send_rtcp_src_pad); send_rtcp_src_pad = null; } if (recv_rtcp_sink_pad != null) { rtpbin.release_request_pad(recv_rtcp_sink_pad); recv_rtcp_sink_pad = null; } } private void prepare_remote_crypto() { if (remote_crypto != null && remote_crypto.is_valid && !crypto_session.has_decrypt) { crypto_session.set_decryption_key(remote_crypto.crypto_suite, remote_crypto.key, remote_crypto.salt); debug("Setting up decryption with key params %s", remote_crypto.key_params); } } private uint16 previous_incoming_video_orientation_degree = uint16.MAX; public signal void incoming_video_orientation_changed(uint16 degree); public override void on_recv_rtp_data(Bytes bytes) { if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) { on_recv_rtcp_data(bytes); return; } #if GST_1_16 { Gst.Buffer buffer = new Gst.Buffer.wrapped_bytes(bytes); Gst.RTP.Buffer rtp_buffer; uint buffer_ssrc = 0, buffer_seq = 0; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { buffer_ssrc = rtp_buffer.get_ssrc(); buffer_seq = rtp_buffer.get_seq(); rtp_buffer.unmap(); } } #endif if (push_recv_data) { prepare_remote_crypto(); Gst.Buffer buffer; if (crypto_session.has_decrypt) { try { buffer = new Gst.Buffer.wrapped(crypto_session.decrypt_rtp(bytes.get_data())); } catch (Error e) { warning("%s (%d)", e.message, e.code); return; } } else { #if GST_1_16 buffer = new Gst.Buffer.wrapped_bytes(bytes); #else buffer = new Gst.Buffer.wrapped(bytes.get_data()); #endif } Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { if (rtp_buffer.get_extension()) { Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation"); if (ext != null) { unowned uint8[] extension_data; if (rtp_buffer.get_extension_onebyte_header(ext.id, 0, out extension_data) && extension_data.length == 1) { bool camera = (extension_data[0] & 0x8) > 0; bool flip = (extension_data[0] & 0x4) > 0; uint8 rotation = extension_data[0] & 0x3; uint16 rotation_degree = uint16.MAX; switch(rotation) { case 0: rotation_degree = 0; break; case 1: rotation_degree = 90; break; case 2: rotation_degree = 180; break; case 3: rotation_degree = 270; break; } if (rotation_degree != previous_incoming_video_orientation_degree) { incoming_video_orientation_changed(rotation_degree); previous_incoming_video_orientation_degree = rotation_degree; } } } } rtp_buffer.unmap(); } #if VALA_0_50 recv_rtp.push_buffer((owned) buffer); #else Gst.FlowReturn ret; GLib.Signal.emit_by_name(recv_rtp, "push-buffer", buffer, out ret); #endif } } public override void on_recv_rtcp_data(Bytes bytes) { if (push_recv_data) { prepare_remote_crypto(); Gst.Buffer buffer; if (crypto_session.has_decrypt) { try { buffer = new Gst.Buffer.wrapped(crypto_session.decrypt_rtcp(bytes.get_data())); } catch (Error e) { warning("%s (%d)", e.message, e.code); return; } } else { #if GST_1_16 buffer = new Gst.Buffer.wrapped_bytes(bytes); #else buffer = new Gst.Buffer.wrapped(bytes.get_data()); #endif } #if VALA_0_50 recv_rtcp.push_buffer((owned) buffer); #else Gst.FlowReturn ret; GLib.Signal.emit_by_name(recv_rtcp, "push-buffer", buffer, out ret); #endif } } public override void on_rtp_ready() { // If full frame has been sent before the connection was ready, the counterpart would only display our video after the next full frame. // Send a full frame to let the counterpart display our video asap rtpbin.send_event(new Gst.Event.custom( Gst.EventType.CUSTOM_UPSTREAM, new Gst.Structure("GstForceKeyUnit", "all-headers", typeof(bool), true, null)) ); } public override void on_rtcp_ready() { int rtp_session_id = (int) rtpid; uint64 max_delay = int.MAX; Object rtp_session; bool rtp_sent; GLib.Signal.emit_by_name(rtpbin, "get-internal-session", rtp_session_id, out rtp_session); GLib.Signal.emit_by_name(rtp_session, "send-rtcp-full", max_delay, out rtp_sent); debug("RTCP is ready, resending rtcp: %s", rtp_sent.to_string()); } public void on_ssrc_pad_added(uint32 ssrc, Gst.Pad pad) { debug("New ssrc %u with pad %s", ssrc, pad.name); if (participant_ssrc != 0 && participant_ssrc != ssrc) { warning("Got second ssrc on stream (old: %u, new: %u), ignoring", participant_ssrc, ssrc); return; } participant_ssrc = ssrc; recv_rtp_src_pad = pad; if (decode != null) { plugin.pause(); debug("Link %s to %s decode for %s", recv_rtp_src_pad.name, media, name); recv_rtp_src_pad.link(decode.get_static_pad("sink")); plugin.unpause(); } } public void on_send_rtp_src_added(Gst.Pad pad) { send_rtp_src_pad = pad; if (send_rtp != null) { plugin.pause(); debug("Link %s to %s send_rtp for %s", send_rtp_src_pad.name, media, name); send_rtp_src_pad.link(send_rtp.get_static_pad("sink")); plugin.unpause(); } } public void set_input(Gst.Element? input) { set_input_and_pause(input, paused); } private void set_input_and_pause(Gst.Element? input, bool paused) { if (created && this.input != null) { this.input_pad.unlink(send_rtp_sink_pad); this.input.release_request_pad(this.input_pad); this.input_pad = null; this.input = null; } this.input = input; this.paused = paused; if (created && sending && !paused && input != null) { plugin.pause(); input_pad = input.get_request_pad(@"src_$rtpid"); input_pad.link(send_rtp_sink_pad); plugin.unpause(); } } public void pause() { if (paused) return; var input = this.input; set_input_and_pause(null, true); if (input != null && input_device != null) input_device.unlink(input); } public void unpause() { if (!paused) return; set_input_and_pause(input_device != null ? input_device.link_source(payload_type, our_ssrc, next_seqnum_offset, next_timestamp_offset) : null, false); input_device.update_bitrate(payload_type, target_send_bitrate); } public uint get_participant_ssrc(Xmpp.Jid participant) { if (participant.equals(content.session.peer_full_jid)) { return participant_ssrc; } return 0; } ulong block_probe_handler_id = 0; public virtual void add_output(Gst.Element element, Xmpp.Jid? participant = null) { if (output != null) { critical("add_output() invoked more than once"); return; } if (participant != null) { critical("add_output() invoked with participant when not supported"); return; } this.output = element; if (created) { plugin.pause(); decode.link(element); if (block_probe_handler_id != 0) { decode.get_static_pad("src").remove_probe(block_probe_handler_id); } plugin.unpause(); } } public virtual void remove_output(Gst.Element element) { if (output != element) { critical("remove_output() invoked without prior add_output()"); return; } if (created) { block_probe_handler_id = decode.get_static_pad("src").add_probe(Gst.PadProbeType.BLOCK, drop_probe); decode.unlink(element); } if (this._output_device != null) { this._output_device.unlink(element); this._output_device = null; } this.output = null; } } public class Dino.Plugins.Rtp.VideoStream : Stream { private Gee.List outputs = new ArrayList(); private Gst.Element output_tee; private Gst.Element rotate; private ulong incoming_video_orientation_changed_handler; public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(plugin, content); if (media != "video") critical("VideoStream created for non-video media"); } public override void create() { incoming_video_orientation_changed_handler = incoming_video_orientation_changed.connect(on_video_orientation_changed); plugin.pause(); rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid"); pipe.add(rotate); output_tee = Gst.ElementFactory.make("tee", @"video_tee_$rtpid"); output_tee.@set("allow-not-linked", true); pipe.add(output_tee); rotate.link(output_tee); add_output(rotate); base.create(); foreach (Gst.Element output in outputs) { output_tee.link(output); } plugin.unpause(); } private void on_video_orientation_changed(uint16 degree) { if (rotate != null) { switch (degree) { case 0: rotate.@set("method", 0); break; case 90: rotate.@set("method", 1); break; case 180: rotate.@set("method", 2); break; case 270: rotate.@set("method", 3); break; } } } public override void destroy() { foreach (Gst.Element output in outputs) { output_tee.unlink(output); } base.destroy(); rotate.set_locked_state(true); rotate.set_state(Gst.State.NULL); rotate.unlink(output_tee); pipe.remove(rotate); rotate = null; output_tee.set_locked_state(true); output_tee.set_state(Gst.State.NULL); pipe.remove(output_tee); output_tee = null; disconnect(incoming_video_orientation_changed_handler); } public override void add_output(Gst.Element element, Xmpp.Jid? participant) { if (element == output_tee || element == rotate) { base.add_output(element); return; } outputs.add(element); if (output_tee != null) { output_tee.link(element); } } public override void remove_output(Gst.Element element) { if (element == output_tee || element == rotate) { base.remove_output(element); return; } outputs.remove(element); if (output_tee != null) { output_tee.unlink(element); } } } dino-0.4.3/plugins/rtp/src/video_widget.vala0000644000000000000000000002445014452563620017554 0ustar rootrootprivate static extern unowned Gst.Video.Info gst_video_frame_get_video_info(Gst.Video.Frame frame); [CCode (array_length_type = "size_t", type = "void*")] private static extern unowned uint8[] gst_video_frame_get_data(Gst.Video.Frame frame); public class Dino.Plugins.Rtp.Paintable : Gdk.Paintable, Object { private Gdk.Paintable image; private double pixel_aspect_ratio; public override Gdk.PaintableFlags get_flags() { return 0; } public void snapshot(Gdk.Snapshot snapshot, double width, double height) { if (image != null) image.snapshot(snapshot, width, height); } public override Gdk.Paintable get_current_image() { if (image != null) return image; return Gdk.Paintable.new_empty(0, 0); } public override int get_intrinsic_width() { if (image != null) return (int) (pixel_aspect_ratio * image.get_intrinsic_width()); return 0; } public override int get_intrinsic_height() { if (image != null) return (int) (pixel_aspect_ratio * image.get_intrinsic_height()); return 0; } public override double get_intrinsic_aspect_ratio() { if (image != null) return pixel_aspect_ratio * image.get_intrinsic_aspect_ratio(); return 0.0; } public override void dispose() { image = null; base.dispose(); } private void set_paintable(Gdk.Paintable paintable, double pixel_aspect_ratio) { if (paintable == image) return; bool size_changed = image == null || this.pixel_aspect_ratio * image.get_intrinsic_width() != pixel_aspect_ratio * paintable.get_intrinsic_width() || image.get_intrinsic_height() != paintable.get_intrinsic_height() || image.get_intrinsic_aspect_ratio() != paintable.get_intrinsic_aspect_ratio(); if (image != null) this.image.dispose(); this.image = paintable; this.pixel_aspect_ratio = pixel_aspect_ratio; if (size_changed) invalidate_size(); invalidate_contents(); } public void queue_set_texture(Gdk.Texture texture, double pixel_aspect_ratio) { Idle.add(() => { set_paintable(texture, pixel_aspect_ratio); return Source.REMOVE; }, Priority.DEFAULT); } } public class Dino.Plugins.Rtp.Sink : Gst.Video.Sink { internal Paintable paintable = new Paintable(); private Gst.Video.Info info = new Gst.Video.Info(); class construct { set_metadata("Dino Gtk Video Sink", "Sink/Video", "The video sink used by Dino", "Dino Team "); add_pad_template(new Gst.PadTemplate("sink", Gst.PadDirection.SINK, Gst.PadPresence.ALWAYS, Gst.Caps.from_string(@"video/x-raw, format={ BGRA, ARGB, RGBA, ABGR, RGB, BGR }"))); } construct { set_drop_out_of_segment(false); } #if GST_1_20 public override bool set_info(Gst.Caps caps, Gst.Video.Info info) { this.info = info; return true; } #else public override bool set_caps(Gst.Caps caps) { base.set_caps(caps); return info.from_caps(caps); } #endif public override void get_times(Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end) { if (buffer.pts != -1) { start = buffer.pts; if (buffer.duration != -1) { end = start + buffer.duration; } else if (info.fps_n > 0) { end = start + Gst.Util.uint64_scale_int(Gst.SECOND, info.fps_d, info.fps_n); } } } public override Gst.Caps get_caps(Gst.Caps? filter) { Gst.Caps caps = Gst.Caps.from_string("video/x-raw, format={ BGRA, ARGB, RGBA, ABGR, RGB, BGR }"); if (filter != null) { return filter.intersect(caps, Gst.CapsIntersectMode.FIRST); } else { return caps; } } private Gdk.MemoryFormat memory_format_from_video(Gst.Video.Format format) { switch (format) { case Gst.Video.Format.BGRA: return Gdk.MemoryFormat.B8G8R8A8; case Gst.Video.Format.ARGB: return Gdk.MemoryFormat.A8R8G8B8; case Gst.Video.Format.RGBA: return Gdk.MemoryFormat.R8G8B8A8; case Gst.Video.Format.ABGR: return Gdk.MemoryFormat.A8B8G8R8; case Gst.Video.Format.RGB: return Gdk.MemoryFormat.R8G8B8; case Gst.Video.Format.BGR: return Gdk.MemoryFormat.B8G8R8; default: warning("Unsupported video format: %s", format.to_string()); return Gdk.MemoryFormat.A8R8G8B8; } } private Gdk.Texture texture_from_buffer(Gst.Buffer buffer, out double pixel_aspect_ratio) { Gst.Video.Frame frame = Gst.Video.Frame(); Gdk.Texture texture; if (frame.map(info, buffer, Gst.MapFlags.READ)) { unowned Gst.Video.Info info = gst_video_frame_get_video_info(frame); Bytes bytes = new Bytes.take(gst_video_frame_get_data(frame)); texture = new Gdk.MemoryTexture(info.width, info.height, memory_format_from_video(info.finfo.format), bytes, info.stride[0]); pixel_aspect_ratio = ((double) info.par_n) / ((double) info.par_d); frame.unmap(); } else { texture = null; } return texture; } private void queue_buffer(Gst.Buffer buf) { double pixel_aspect_ratio; Gdk.Texture texture = texture_from_buffer(buf, out pixel_aspect_ratio); if (texture != null) { paintable.queue_set_texture(texture, pixel_aspect_ratio); } } public override Gst.FlowReturn show_frame(Gst.Buffer buf) { @lock.lock(); queue_buffer(buf); @lock.unlock(); return Gst.FlowReturn.OK; } } public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWidget { private const int RECAPS_AFTER_CHANGE = 5; private static uint last_id = 0; public uint id { get; private set; } public Plugin plugin { get; private set; } public Gst.Pipeline pipe { get { return plugin.pipe; }} private bool attached; private Device? connected_device; private Gst.Element? connected_device_element; private Stream? connected_stream; private Gst.Element prepare; private Gst.Caps last_input_caps; private Gst.Caps last_caps; private int recaps_since_change; private Sink sink; private Gtk.Picture widget; public VideoWidget(Plugin plugin) { this.plugin = plugin; this.layout_manager = new Gtk.BinLayout(); id = last_id++; sink = new Sink() { async = false, sync = true }; widget = new Gtk.Picture.for_paintable(sink.paintable); widget.insert_after(this, null); } public void input_caps_changed(GLib.Object pad, ParamSpec spec) { Gst.Caps? caps = ((Gst.Pad)pad).caps; if (caps == null) { debug("Input: No caps"); return; } int width, height; caps.get_structure(0).get_int("width", out width); caps.get_structure(0).get_int("height", out height); debug("Input resolution changed: %ix%i", width, height); // Invoke signal on GTK main loop as recipients are likely to use it for doing GTK operations Idle.add(() => { resolution_changed(width, height); return Source.REMOVE; }); last_input_caps = caps; } public void display_stream(Xmpp.Xep.JingleRtp.Stream? stream, Xmpp.Jid jid) { if (sink == null) return; detach(); if (stream.media != "video") return; connected_stream = stream as Stream?; if (connected_stream == null) return; plugin.pause(); pipe.add(sink); prepare = Gst.parse_bin_from_description(@"videoconvert name=video_widget_$(id)_convert", true); prepare.name = @"video_widget_$(id)_prepare"; prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); pipe.add(prepare); connected_stream.add_output(prepare); prepare.link(sink); sink.set_locked_state(false); plugin.unpause(); attached = true; } public void display_device(MediaDevice media_device) { if (sink == null) return; detach(); connected_device = media_device as Device; if (connected_device == null) return; plugin.pause(); pipe.add(sink); #if GST_1_20 prepare = Gst.parse_bin_from_description(@"videoflip video-direction=auto name=video_widget_$(id)_orientation ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); #else prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); #endif prepare.name = @"video_widget_$(id)_prepare"; #if GST_1_20 if (prepare is Gst.Bin) { ((Gst.Bin) prepare).get_by_name(@"video_widget_$(id)_flip").get_static_pad("sink").notify["caps"].connect(input_caps_changed); } else { #endif prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); #if GST_1_20 } #endif pipe.add(prepare); connected_device_element = connected_device.link_source(); connected_device_element.link(prepare); prepare.link(sink); sink.set_locked_state(false); plugin.unpause(); attached = true; } public void detach() { if (sink == null) return; if (attached) { debug("Detaching"); if (connected_stream != null) { connected_stream.remove_output(prepare); connected_stream = null; } if (connected_device != null) { connected_device_element.unlink(sink); connected_device_element = null; connected_device.unlink(); connected_device = null; } prepare.set_locked_state(true); prepare.set_state(Gst.State.NULL); pipe.remove(prepare); prepare = null; sink.set_locked_state(true); sink.set_state(Gst.State.NULL); pipe.remove(sink); attached = false; } } public override void dispose() { detach(); if (widget != null) widget.unparent(); widget = null; sink = null; } } dino-0.4.3/plugins/rtp/src/voice_processor.vala0000644000000000000000000001571314452563620020311 0ustar rootrootusing Gst; namespace Dino.Plugins.Rtp { public static extern Buffer adjust_to_running_time(Base.Transform transform, Buffer buf); } public class Dino.Plugins.Rtp.EchoProbe : Audio.Filter { private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; public Audio.Info audio_info { get; private set; } public signal void on_new_buffer(Buffer buffer); private uint period_samples; private uint period_size; private Base.Adapter adapter = new Base.Adapter(); static construct { add_static_pad_template(sink_template); add_static_pad_template(src_template); set_static_metadata("Acoustic Echo Canceller probe", "Generic/Audio", "Gathers playback buffers for echo cancellation", "Dino Team "); } construct { set_passthrough(true); } public override bool setup(Audio.Info info) { audio_info = info; period_samples = info.rate / 100; // 10ms buffers period_size = period_samples * info.bpf; return true; } public override FlowReturn transform_ip(Buffer buf) { lock (adapter) { adapter.push(adjust_to_running_time(this, buf)); while (adapter.available() > period_size) { on_new_buffer(adapter.take_buffer(period_size)); } } return FlowReturn.OK; } public override bool stop() { adapter.clear(); return true; } } public class Dino.Plugins.Rtp.VoiceProcessor : Audio.Filter { private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; public Audio.Info audio_info { get; private set; } private ulong process_outgoing_buffer_handler_id; private uint adjust_delay_timeout_id; private uint period_samples; private uint period_size; private Base.Adapter adapter = new Base.Adapter(); private EchoProbe? echo_probe; private Audio.StreamVolume? stream_volume; private ClockTime last_reverse; private void* native; static construct { add_static_pad_template(sink_template); add_static_pad_template(src_template); set_static_metadata("Voice Processor (AGC, AEC, filters, etc.)", "Generic/Audio", "Pre-processes voice with WebRTC Audio Processing Library", "Dino Team "); } construct { set_passthrough(false); } public VoiceProcessor(EchoProbe? echo_probe = null, Audio.StreamVolume? stream_volume = null) { this.echo_probe = echo_probe; this.stream_volume = stream_volume; } private static extern void* init_native(int stream_delay); private static extern void setup_native(void* native); private static extern void destroy_native(void* native); private static extern void analyze_reverse_stream(void* native, Audio.Info info, Buffer buffer); private static extern void process_stream(void* native, Audio.Info info, Buffer buffer); private static extern void adjust_stream_delay(void* native); private static extern void notify_gain_level(void* native, int gain_level); private static extern int get_suggested_gain_level(void* native); private static extern bool get_stream_has_voice(void* native); public override bool setup(Audio.Info info) { debug("VoiceProcessor.setup(%s)", info.to_caps().to_string()); audio_info = info; period_samples = info.rate / 100; // 10ms buffers period_size = period_samples * info.bpf; adapter.clear(); setup_native(native); return true; } public override bool start() { native = init_native(150); if (process_outgoing_buffer_handler_id == 0 && echo_probe != null) { process_outgoing_buffer_handler_id = echo_probe.on_new_buffer.connect(process_outgoing_buffer); } if (stream_volume == null && sinkpad.get_peer() != null && sinkpad.get_peer().get_parent_element() is Audio.StreamVolume) { stream_volume = sinkpad.get_peer().get_parent_element() as Audio.StreamVolume; } return true; } private bool adjust_delay() { if (native != null) { adjust_stream_delay(native); return Source.CONTINUE; } else { adjust_delay_timeout_id = 0; return Source.REMOVE; } } private void process_outgoing_buffer(Buffer buffer) { if (buffer.pts != uint64.MAX) { last_reverse = buffer.pts; } analyze_reverse_stream(native, echo_probe.audio_info, buffer); if (adjust_delay_timeout_id == 0 && echo_probe != null) { adjust_delay_timeout_id = Timeout.add(1000, adjust_delay); } } public override FlowReturn submit_input_buffer(bool is_discont, Buffer input) { lock (adapter) { if (is_discont) { adapter.clear(); } adapter.push(adjust_to_running_time(this, input)); } return FlowReturn.OK; } public override FlowReturn generate_output(out Buffer output_buffer) { lock (adapter) { if (adapter.available() >= period_size) { output_buffer = (Gst.Buffer) adapter.take_buffer(period_size).make_writable(); int old_gain_level = 0; if (stream_volume != null) { old_gain_level = (int) (stream_volume.get_volume(Audio.StreamVolumeFormat.LINEAR) * 255.0); notify_gain_level(native, old_gain_level); } process_stream(native, audio_info, output_buffer); if (stream_volume != null) { int new_gain_level = get_suggested_gain_level(native); if (old_gain_level != new_gain_level) { debug("Gain: %i -> %i", old_gain_level, new_gain_level); stream_volume.set_volume(Audio.StreamVolumeFormat.LINEAR, ((double)new_gain_level) / 255.0); } } } } return FlowReturn.OK; } public override bool stop() { if (process_outgoing_buffer_handler_id != 0) { echo_probe.disconnect(process_outgoing_buffer_handler_id); process_outgoing_buffer_handler_id = 0; } if (adjust_delay_timeout_id != 0) { Source.remove(adjust_delay_timeout_id); adjust_delay_timeout_id = 0; } adapter.clear(); destroy_native(native); native = null; return true; } }dino-0.4.3/plugins/rtp/src/voice_processor_native.cpp0000644000000000000000000001552314452563620021515 0ustar rootroot#include #include #include #include #include #include #define SAMPLE_RATE 48000 #define SAMPLE_CHANNELS 1 struct _DinoPluginsRtpVoiceProcessorNative { webrtc::AudioProcessing *apm; gint stream_delay; gint last_median; gint last_poor_delays; }; extern "C" void *dino_plugins_rtp_adjust_to_running_time(GstBaseTransform *transform, GstBuffer *buffer) { GstBuffer *copy = gst_buffer_copy(buffer); GST_BUFFER_PTS(copy) = gst_segment_to_running_time(&transform->segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer)); return copy; } extern "C" void *dino_plugins_rtp_voice_processor_init_native(gint stream_delay) { _DinoPluginsRtpVoiceProcessorNative *native = new _DinoPluginsRtpVoiceProcessorNative(); webrtc::Config config; config.Set(new webrtc::ExtendedFilter(true)); config.Set(new webrtc::ExperimentalAgc(true, 85)); native->apm = webrtc::AudioProcessing::Create(config); native->stream_delay = stream_delay; native->last_median = 0; native->last_poor_delays = 0; return native; } extern "C" void dino_plugins_rtp_voice_processor_setup_native(void *native_ptr) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::AudioProcessing *apm = native->apm; webrtc::ProcessingConfig pconfig; pconfig.streams[webrtc::ProcessingConfig::kInputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kOutputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kReverseInputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kReverseOutputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); apm->Initialize(pconfig); apm->high_pass_filter()->Enable(true); apm->echo_cancellation()->enable_drift_compensation(false); apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kModerateSuppression); apm->echo_cancellation()->enable_delay_logging(true); apm->echo_cancellation()->Enable(true); apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kModerate); apm->noise_suppression()->Enable(true); apm->gain_control()->set_analog_level_limits(0, 255); apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog); apm->gain_control()->set_target_level_dbfs(3); apm->gain_control()->set_compression_gain_db(9); apm->gain_control()->enable_limiter(true); apm->gain_control()->Enable(true); apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kLowLikelihood); apm->voice_detection()->Enable(true); } extern "C" void dino_plugins_rtp_voice_processor_analyze_reverse_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); webrtc::AudioProcessing *apm = native->apm; GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READ); webrtc::AudioFrame frame; frame.num_channels_ = info->channels; frame.sample_rate_hz_ = info->rate; frame.samples_per_channel_ = gst_buffer_get_size(buffer) / info->bpf; memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf); int err = apm->AnalyzeReverseStream(&frame); if (err < 0) g_warning("voice_processor_native.cpp: ProcessReverseStream %i", err); gst_buffer_unmap(buffer, &map); } extern "C" void dino_plugins_rtp_voice_processor_notify_gain_level(void *native_ptr, gint gain_level) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::AudioProcessing *apm = native->apm; apm->gain_control()->set_stream_analog_level(gain_level); } extern "C" gint dino_plugins_rtp_voice_processor_get_suggested_gain_level(void *native_ptr) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::AudioProcessing *apm = native->apm; return apm->gain_control()->stream_analog_level(); } extern "C" bool dino_plugins_rtp_voice_processor_get_stream_has_voice(void *native_ptr) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::AudioProcessing *apm = native->apm; return apm->voice_detection()->stream_has_voice(); } extern "C" void dino_plugins_rtp_voice_processor_adjust_stream_delay(void *native_ptr) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::AudioProcessing *apm = native->apm; int median, std, poor_delays; float fraction_poor_delays; apm->echo_cancellation()->GetDelayMetrics(&median, &std, &fraction_poor_delays); poor_delays = (int)(fraction_poor_delays * 100.0); if (fraction_poor_delays < 0 || (native->last_median == median && native->last_poor_delays == poor_delays)) return; g_debug("voice_processor_native.cpp: Stream delay metrics: median=%i std=%i poor_delays=%i%%", median, std, poor_delays); native->last_median = median; native->last_poor_delays = poor_delays; if (poor_delays > 90) { native->stream_delay = std::min(std::max(0, native->stream_delay + std::min(48, std::max(median, -48))), 384); g_debug("voice_processor_native.cpp: set stream_delay=%i", native->stream_delay); } } extern "C" void dino_plugins_rtp_voice_processor_process_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); webrtc::AudioProcessing *apm = native->apm; GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READWRITE); webrtc::AudioFrame frame; frame.num_channels_ = info->channels; frame.sample_rate_hz_ = info->rate; frame.samples_per_channel_ = info->rate / 100; memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf); apm->set_stream_delay_ms(native->stream_delay); int err = apm->ProcessStream(&frame); if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf); if (err < 0) g_warning("voice_processor_native.cpp: ProcessStream %i", err); gst_buffer_unmap(buffer, &map); } extern "C" void dino_plugins_rtp_voice_processor_destroy_native(void *native_ptr) { _DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; delete native; }dino-0.4.3/plugins/rtp/vapi/0000755000000000000000000000000014452563620014401 5ustar rootrootdino-0.4.3/plugins/rtp/vapi/gstreamer-base-1.0.vapi0000644000000000000000000013742414452563620020472 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-base-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstBase", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace Base { [CCode (cheader_filename = "gst/base/base.h", cname = "GstAdapter", lower_case_cprefix = "gst_adapter_", type_id = "gst_adapter_get_type ()")] [GIR (name = "Adapter")] public class Adapter : GLib.Object { [CCode (has_construct_function = false)] public Adapter (); public size_t available (); public size_t available_fast (); public void clear (); public void copy ([CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "gsize")] uint8[] dest, size_t offset); [Version (since = "1.4")] public GLib.Bytes copy_bytes (size_t offset, size_t size); [Version (since = "1.10")] public uint64 distance_from_discont (); [Version (since = "1.10")] public Gst.ClockTime dts_at_discont (); public void flush (size_t flush); [Version (since = "1.6")] public Gst.Buffer? get_buffer (size_t nbytes); [Version (since = "1.6")] public Gst.Buffer? get_buffer_fast (size_t nbytes); [Version (since = "1.6")] public Gst.BufferList? get_buffer_list (size_t nbytes); [Version (since = "1.6")] public GLib.List? get_list (size_t nbytes); [CCode (array_length = false)] public unowned uint8[]? map (size_t size); public ssize_t masked_scan_uint32 (uint32 mask, uint32 pattern, size_t offset, size_t size); public ssize_t masked_scan_uint32_peek (uint32 mask, uint32 pattern, size_t offset, size_t size, out uint32 value); [Version (since = "1.10")] public uint64 offset_at_discont (); public Gst.ClockTime prev_dts (out uint64 distance); [Version (since = "1.2")] public Gst.ClockTime prev_dts_at_offset (size_t offset, out uint64 distance); [Version (since = "1.10")] public uint64 prev_offset (out uint64 distance); public Gst.ClockTime prev_pts (out uint64 distance); [Version (since = "1.2")] public Gst.ClockTime prev_pts_at_offset (size_t offset, out uint64 distance); [Version (since = "1.10")] public Gst.ClockTime pts_at_discont (); public void push (owned Gst.Buffer buf); [CCode (array_length = false)] public uint8[]? take (size_t nbytes); public Gst.Buffer? take_buffer (size_t nbytes); [Version (since = "1.2")] public Gst.Buffer? take_buffer_fast (size_t nbytes); [Version (since = "1.6")] public Gst.BufferList? take_buffer_list (size_t nbytes); public GLib.List? take_list (size_t nbytes); public void unmap (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregator", lower_case_cprefix = "gst_aggregator_", type_id = "gst_aggregator_get_type ()")] [GIR (name = "Aggregator")] [Version (since = "1.14")] public abstract class Aggregator : Gst.Element { public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Aggregator (); [NoWrapper] public virtual Gst.FlowReturn aggregate (bool timeout); [NoWrapper] public virtual Gst.Buffer clip (Gst.Base.AggregatorPad aggregator_pad, Gst.Buffer buf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); public virtual Gst.FlowReturn finish_buffer (owned Gst.Buffer buffer); [Version (since = "1.18")] public virtual Gst.FlowReturn finish_buffer_list (owned Gst.BufferList bufferlist); [NoWrapper] public virtual Gst.Caps fixate_src_caps (Gst.Caps caps); [NoWrapper] public virtual Gst.FlowReturn flush (); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public Gst.BufferPool? get_buffer_pool (); [Version (since = "1.20")] public bool get_ignore_inactive_pads (); public Gst.ClockTime get_latency (); [NoWrapper] public virtual Gst.ClockTime get_next_time (); [Version (since = "1.18")] public virtual bool negotiate (); [NoWrapper] public virtual bool negotiated_src_caps (Gst.Caps caps); [Version (since = "1.18")] public virtual Gst.Sample? peek_next_sample (Gst.Base.AggregatorPad aggregator_pad); [NoWrapper] public virtual bool propose_allocation (Gst.Base.AggregatorPad pad, Gst.Query decide_query, Gst.Query query); [Version (since = "1.18")] public void selected_samples (Gst.ClockTime pts, Gst.ClockTime dts, Gst.ClockTime duration, Gst.Structure? info); [Version (since = "1.20")] public void set_ignore_inactive_pads (bool ignore); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_src_caps (Gst.Caps caps); [Version (since = "1.16")] public Gst.ClockTime simple_get_next_time (); [NoWrapper] public virtual bool sink_event (Gst.Base.AggregatorPad aggregator_pad, Gst.Event event); [NoWrapper] public virtual Gst.FlowReturn sink_event_pre_queue (Gst.Base.AggregatorPad aggregator_pad, Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Base.AggregatorPad aggregator_pad, Gst.Query query); [NoWrapper] public virtual bool sink_query_pre_queue (Gst.Base.AggregatorPad aggregator_pad, Gst.Query query); [NoWrapper] public virtual bool src_activate (Gst.PadMode mode, bool active); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [Version (since = "1.18")] public void update_segment (Gst.Segment segment); [NoWrapper] public virtual Gst.FlowReturn update_src_caps (Gst.Caps caps, out Gst.Caps ret); [NoAccessorMethod] [Version (since = "1.18")] public bool emit_signals { get; set; } [NoAccessorMethod] public uint64 latency { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public uint64 min_upstream_latency { get; set; } [NoAccessorMethod] public uint64 start_time { get; set; } [NoAccessorMethod] public Gst.Base.AggregatorStartTimeSelection start_time_selection { get; set; } [Version (since = "1.18")] public signal void samples_selected (Gst.Segment segment, uint64 pts, uint64 dts, uint64 duration, Gst.Structure? info); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregatorPad", lower_case_cprefix = "gst_aggregator_pad_", type_id = "gst_aggregator_pad_get_type ()")] [GIR (name = "AggregatorPad")] [Version (since = "1.14")] public class AggregatorPad : Gst.Pad { public weak Gst.Segment segment; [CCode (has_construct_function = false)] protected AggregatorPad (); public bool drop_buffer (); [NoWrapper] public virtual Gst.FlowReturn flush (Gst.Base.Aggregator aggregator); [Version (since = "1.14.1")] public bool has_buffer (); public bool is_eos (); [Version (since = "1.20")] public bool is_inactive (); public Gst.Buffer? peek_buffer (); public Gst.Buffer? pop_buffer (); [NoWrapper] public virtual bool skip_buffer (Gst.Base.Aggregator aggregator, Gst.Buffer buffer); [NoAccessorMethod] [Version (since = "1.16")] public bool emit_signals { get; set; } public signal void buffer_consumed (Gst.Buffer object); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstBitReader", free_function = "gst_bit_reader_free", has_type_id = false)] [Compact] [GIR (name = "BitReader")] public class BitReader { public uint bit; public uint byte; [CCode (array_length_cname = "size", array_length_type = "guint")] public weak uint8[] data; public uint size; [CCode (cname = "gst_bit_reader_new", has_construct_function = false)] public BitReader ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_reader_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_bit_reader_get_bits_uint16")] public bool get_bits_uint16 (out uint16 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint32")] public bool get_bits_uint32 (out uint32 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint64")] public bool get_bits_uint64 (out uint64 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint8")] public bool get_bits_uint8 (out uint8 val, uint nbits); [CCode (cname = "gst_bit_reader_get_pos")] public uint get_pos (); [CCode (cname = "gst_bit_reader_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_bit_reader_get_size")] public uint get_size (); [CCode (cname = "gst_bit_reader_init")] public void init ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_reader_peek_bits_uint16")] public bool peek_bits_uint16 (out uint16 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint32")] public bool peek_bits_uint32 (out uint32 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint64")] public bool peek_bits_uint64 (out uint64 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint8")] public bool peek_bits_uint8 (out uint8 val, uint nbits); [CCode (cname = "gst_bit_reader_set_pos")] public bool set_pos (uint pos); [CCode (cname = "gst_bit_reader_skip")] public bool skip (uint nbits); [CCode (cname = "gst_bit_reader_skip_to_byte")] public bool skip_to_byte (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstByteReader", free_function = "gst_byte_reader_free", has_type_id = false)] [Compact] [GIR (name = "ByteReader")] public class ByteReader { public uint byte; [CCode (array_length_cname = "size", array_length_type = "guint")] public weak uint8[] data; public uint size; [CCode (cname = "gst_byte_reader_new", has_construct_function = false)] public ByteReader ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_reader_dup_data")] public bool dup_data (uint size, out uint8[] val); [CCode (cname = "gst_byte_reader_dup_string_utf16")] public bool dup_string_utf16 ([CCode (array_length = false, array_null_terminated = true)] out uint16[] str); [CCode (cname = "gst_byte_reader_dup_string_utf32")] public bool dup_string_utf32 ([CCode (array_length = false, array_null_terminated = true)] out uint32[] str); [CCode (cname = "gst_byte_reader_dup_string_utf8")] public bool dup_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out string[] str); [CCode (cname = "gst_byte_reader_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_byte_reader_get_data")] public bool get_data (uint size, out unowned uint8[] val); [CCode (cname = "gst_byte_reader_get_float32_be")] public bool get_float32_be (out float val); [CCode (cname = "gst_byte_reader_get_float32_le")] public bool get_float32_le (out float val); [CCode (cname = "gst_byte_reader_get_float64_be")] public bool get_float64_be (out double val); [CCode (cname = "gst_byte_reader_get_float64_le")] public bool get_float64_le (out double val); [CCode (cname = "gst_byte_reader_get_int16_be")] public bool get_int16_be (out int16 val); [CCode (cname = "gst_byte_reader_get_int16_le")] public bool get_int16_le (out int16 val); [CCode (cname = "gst_byte_reader_get_int24_be")] public bool get_int24_be (out int32 val); [CCode (cname = "gst_byte_reader_get_int24_le")] public bool get_int24_le (out int32 val); [CCode (cname = "gst_byte_reader_get_int32_be")] public bool get_int32_be (out int32 val); [CCode (cname = "gst_byte_reader_get_int32_le")] public bool get_int32_le (out int32 val); [CCode (cname = "gst_byte_reader_get_int64_be")] public bool get_int64_be (out int64 val); [CCode (cname = "gst_byte_reader_get_int64_le")] public bool get_int64_le (out int64 val); [CCode (cname = "gst_byte_reader_get_int8")] public bool get_int8 (out int8 val); [CCode (cname = "gst_byte_reader_get_pos")] public uint get_pos (); [CCode (cname = "gst_byte_reader_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_byte_reader_get_size")] public uint get_size (); [CCode (cname = "gst_byte_reader_get_string_utf8")] public bool get_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out unowned string[] str); [CCode (cname = "gst_byte_reader_get_uint16_be")] public bool get_uint16_be (out uint16 val); [CCode (cname = "gst_byte_reader_get_uint16_le")] public bool get_uint16_le (out uint16 val); [CCode (cname = "gst_byte_reader_get_uint24_be")] public bool get_uint24_be (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint24_le")] public bool get_uint24_le (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint32_be")] public bool get_uint32_be (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint32_le")] public bool get_uint32_le (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint64_be")] public bool get_uint64_be (out uint64 val); [CCode (cname = "gst_byte_reader_get_uint64_le")] public bool get_uint64_le (out uint64 val); [CCode (cname = "gst_byte_reader_get_uint8")] public bool get_uint8 (out uint8 val); [CCode (cname = "gst_byte_reader_init")] public void init ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_reader_masked_scan_uint32")] public uint masked_scan_uint32 (uint32 mask, uint32 pattern, uint offset, uint size); [CCode (cname = "gst_byte_reader_masked_scan_uint32_peek")] [Version (since = "1.6")] public uint masked_scan_uint32_peek (uint32 mask, uint32 pattern, uint offset, uint size, out uint32 value); [CCode (cname = "gst_byte_reader_peek_data")] public bool peek_data (uint size, out unowned uint8[] val); [CCode (cname = "gst_byte_reader_peek_float32_be")] public bool peek_float32_be (out float val); [CCode (cname = "gst_byte_reader_peek_float32_le")] public bool peek_float32_le (out float val); [CCode (cname = "gst_byte_reader_peek_float64_be")] public bool peek_float64_be (out double val); [CCode (cname = "gst_byte_reader_peek_float64_le")] public bool peek_float64_le (out double val); [CCode (cname = "gst_byte_reader_peek_int16_be")] public bool peek_int16_be (out int16 val); [CCode (cname = "gst_byte_reader_peek_int16_le")] public bool peek_int16_le (out int16 val); [CCode (cname = "gst_byte_reader_peek_int24_be")] public bool peek_int24_be (out int32 val); [CCode (cname = "gst_byte_reader_peek_int24_le")] public bool peek_int24_le (out int32 val); [CCode (cname = "gst_byte_reader_peek_int32_be")] public bool peek_int32_be (out int32 val); [CCode (cname = "gst_byte_reader_peek_int32_le")] public bool peek_int32_le (out int32 val); [CCode (cname = "gst_byte_reader_peek_int64_be")] public bool peek_int64_be (out int64 val); [CCode (cname = "gst_byte_reader_peek_int64_le")] public bool peek_int64_le (out int64 val); [CCode (cname = "gst_byte_reader_peek_int8")] public bool peek_int8 (out int8 val); [CCode (cname = "gst_byte_reader_peek_string_utf8")] public bool peek_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out unowned string[] str); [CCode (cname = "gst_byte_reader_peek_uint16_be")] public bool peek_uint16_be (out uint16 val); [CCode (cname = "gst_byte_reader_peek_uint16_le")] public bool peek_uint16_le (out uint16 val); [CCode (cname = "gst_byte_reader_peek_uint24_be")] public bool peek_uint24_be (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint24_le")] public bool peek_uint24_le (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint32_be")] public bool peek_uint32_be (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint32_le")] public bool peek_uint32_le (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint64_be")] public bool peek_uint64_be (out uint64 val); [CCode (cname = "gst_byte_reader_peek_uint64_le")] public bool peek_uint64_le (out uint64 val); [CCode (cname = "gst_byte_reader_peek_uint8")] public bool peek_uint8 (out uint8 val); [CCode (cname = "gst_byte_reader_set_pos")] public bool set_pos (uint pos); [CCode (cname = "gst_byte_reader_skip")] public bool skip (uint nbytes); [CCode (cname = "gst_byte_reader_skip_string_utf16")] public bool skip_string_utf16 (); [CCode (cname = "gst_byte_reader_skip_string_utf32")] public bool skip_string_utf32 (); [CCode (cname = "gst_byte_reader_skip_string_utf8")] public bool skip_string_utf8 (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstByteWriter", free_function = "gst_byte_writer_free", has_type_id = false)] [Compact] [GIR (name = "ByteWriter")] public class ByteWriter { public uint alloc_size; public bool fixed; public bool @owned; public weak Gst.Base.ByteReader parent; [CCode (cname = "gst_byte_writer_new", has_construct_function = false)] public ByteWriter (); [CCode (cname = "gst_byte_writer_ensure_free_space")] public bool ensure_free_space (uint size); [CCode (cname = "gst_byte_writer_fill")] public bool fill (uint8 value, uint size); [CCode (cname = "gst_byte_writer_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_byte_writer_free_and_get_buffer")] [DestroysInstance] public Gst.Buffer free_and_get_buffer (); [CCode (cname = "gst_byte_writer_free_and_get_data")] [DestroysInstance] public uint8 free_and_get_data (); [CCode (cname = "gst_byte_writer_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_byte_writer_init")] public void init (); [CCode (cname = "gst_byte_writer_init_with_data")] public void init_with_data ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, bool initialized); [CCode (cname = "gst_byte_writer_init_with_size")] public void init_with_size (uint size, bool fixed); [CCode (cname = "gst_byte_writer_put_buffer")] public bool put_buffer (Gst.Buffer buffer, size_t offset, ssize_t size); [CCode (cname = "gst_byte_writer_put_data")] public bool put_data ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_writer_put_float32_be")] public bool put_float32_be (float val); [CCode (cname = "gst_byte_writer_put_float32_le")] public bool put_float32_le (float val); [CCode (cname = "gst_byte_writer_put_float64_be")] public bool put_float64_be (double val); [CCode (cname = "gst_byte_writer_put_float64_le")] public bool put_float64_le (double val); [CCode (cname = "gst_byte_writer_put_int16_be")] public bool put_int16_be (int16 val); [CCode (cname = "gst_byte_writer_put_int16_le")] public bool put_int16_le (int16 val); [CCode (cname = "gst_byte_writer_put_int24_be")] public bool put_int24_be (int32 val); [CCode (cname = "gst_byte_writer_put_int24_le")] public bool put_int24_le (int32 val); [CCode (cname = "gst_byte_writer_put_int32_be")] public bool put_int32_be (int32 val); [CCode (cname = "gst_byte_writer_put_int32_le")] public bool put_int32_le (int32 val); [CCode (cname = "gst_byte_writer_put_int64_be")] public bool put_int64_be (int64 val); [CCode (cname = "gst_byte_writer_put_int64_le")] public bool put_int64_le (int64 val); [CCode (cname = "gst_byte_writer_put_int8")] public bool put_int8 (int8 val); [CCode (cname = "gst_byte_writer_put_string_utf16")] public bool put_string_utf16 ([CCode (array_length = false, array_null_terminated = true)] uint16[] data); [CCode (cname = "gst_byte_writer_put_string_utf32")] public bool put_string_utf32 ([CCode (array_length = false, array_null_terminated = true)] uint32[] data); [CCode (cname = "gst_byte_writer_put_string_utf8")] public bool put_string_utf8 (string data); [CCode (cname = "gst_byte_writer_put_uint16_be")] public bool put_uint16_be (uint16 val); [CCode (cname = "gst_byte_writer_put_uint16_le")] public bool put_uint16_le (uint16 val); [CCode (cname = "gst_byte_writer_put_uint24_be")] public bool put_uint24_be (uint32 val); [CCode (cname = "gst_byte_writer_put_uint24_le")] public bool put_uint24_le (uint32 val); [CCode (cname = "gst_byte_writer_put_uint32_be")] public bool put_uint32_be (uint32 val); [CCode (cname = "gst_byte_writer_put_uint32_le")] public bool put_uint32_le (uint32 val); [CCode (cname = "gst_byte_writer_put_uint64_be")] public bool put_uint64_be (uint64 val); [CCode (cname = "gst_byte_writer_put_uint64_le")] public bool put_uint64_le (uint64 val); [CCode (cname = "gst_byte_writer_put_uint8")] public bool put_uint8 (uint8 val); [CCode (cname = "gst_byte_writer_reset")] public void reset (); [CCode (cname = "gst_byte_writer_reset_and_get_buffer")] public Gst.Buffer reset_and_get_buffer (); [CCode (array_length = false, cname = "gst_byte_writer_reset_and_get_data")] public uint8[] reset_and_get_data (); [CCode (cname = "gst_byte_writer_new_with_data", has_construct_function = false)] public ByteWriter.with_data ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint", type = "guint8*")] uint8[] data, bool initialized); [CCode (cname = "gst_byte_writer_new_with_size", has_construct_function = false)] public ByteWriter.with_size (uint size, bool fixed); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPads", lower_case_cprefix = "gst_collect_pads_", type_id = "gst_collect_pads_get_type ()")] [GIR (name = "CollectPads")] public class CollectPads : Gst.Object { public weak GLib.SList data; [CCode (has_construct_function = false)] public CollectPads (); public unowned Gst.Base.CollectData? add_pad (Gst.Pad pad, uint size, [CCode (scope = "async")] Gst.Base.CollectDataDestroyNotify destroy_notify, bool @lock); public uint available (); public Gst.FlowReturn clip_running_time (Gst.Base.CollectData cdata, Gst.Buffer buf, out Gst.Buffer outbuf, void* user_data); public bool event_default (Gst.Base.CollectData data, Gst.Event event, bool discard); public uint flush (Gst.Base.CollectData data, uint size); public Gst.Buffer? peek (Gst.Base.CollectData data); public Gst.Buffer? pop (Gst.Base.CollectData data); public bool query_default (Gst.Base.CollectData data, Gst.Query query, bool discard); public Gst.Buffer? read_buffer (Gst.Base.CollectData data, uint size); public bool remove_pad (Gst.Pad pad); public void set_buffer_function (Gst.Base.CollectPadsBufferFunction func); public void set_clip_function (Gst.Base.CollectPadsClipFunction clipfunc); public void set_compare_function (Gst.Base.CollectPadsCompareFunction func); public void set_event_function (Gst.Base.CollectPadsEventFunction func); [Version (since = "1.4")] public void set_flush_function (Gst.Base.CollectPadsFlushFunction func); public void set_flushing (bool flushing); public void set_function (Gst.Base.CollectPadsFunction func); public void set_query_function (Gst.Base.CollectPadsQueryFunction func); public void set_waiting (Gst.Base.CollectData data, bool waiting); [Version (since = "1.4")] public bool src_event_default (Gst.Pad pad, Gst.Event event); public void start (); public void stop (); public Gst.Buffer? take_buffer (Gst.Base.CollectData data, uint size); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueue", lower_case_cprefix = "gst_data_queue_", type_id = "gst_data_queue_get_type ()")] [GIR (name = "DataQueue")] public class DataQueue : GLib.Object { [CCode (has_construct_function = false)] protected DataQueue (); [NoWrapper] public virtual void empty (); [NoWrapper] public virtual void full (); [NoAccessorMethod] public uint current_level_bytes { get; } [NoAccessorMethod] public uint64 current_level_time { get; } [NoAccessorMethod] public uint current_level_visible { get; } } [CCode (cheader_filename = "gst/base/base.h", cname = "GstFlowCombiner", copy_function = "g_boxed_copy", free_function = "g_boxed_free", lower_case_cprefix = "gst_flow_combiner_", type_id = "gst_flow_combiner_get_type ()")] [Compact] [GIR (name = "FlowCombiner")] [Version (since = "1.4")] public class FlowCombiner { [CCode (has_construct_function = false)] public FlowCombiner (); public void add_pad (Gst.Pad pad); [Version (since = "1.6")] public void clear (); public void free (); [Version (since = "1.12.1")] public unowned Gst.Base.FlowCombiner @ref (); public void remove_pad (Gst.Pad pad); [Version (since = "1.6")] public void reset (); [Version (since = "1.12.1")] public void unref (); public Gst.FlowReturn update_flow (Gst.FlowReturn fret); [Version (since = "1.6")] public Gst.FlowReturn update_pad_flow (Gst.Pad pad, Gst.FlowReturn fret); } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_parse_get_type ()")] [GIR (name = "BaseParse")] public abstract class Parse : Gst.Element { public uint flags; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Parse (); public bool add_index_entry (uint64 offset, Gst.ClockTime ts, bool key, bool force); [NoWrapper] public virtual bool convert (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); public bool convert_default (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); [NoWrapper] public virtual Gst.FlowReturn detect (Gst.Buffer buffer); [Version (since = "1.12")] public void drain (); public Gst.FlowReturn finish_frame (Gst.Base.ParseFrame frame, int size); [NoWrapper] public virtual Gst.Caps get_sink_caps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (Gst.Base.ParseFrame frame, out int skipsize); [Version (since = "1.6")] public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); [NoWrapper] public virtual Gst.FlowReturn pre_push_frame (Gst.Base.ParseFrame frame); public Gst.FlowReturn push_frame (Gst.Base.ParseFrame frame); public void set_average_bitrate (uint bitrate); public void set_duration (Gst.Format fmt, int64 duration, int interval); public void set_frame_rate (uint fps_num, uint fps_den, uint lead_in, uint lead_out); public void set_has_timing_info (bool has_timing); public void set_infer_ts (bool infer_ts); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_min_frame_size (uint min_size); public void set_passthrough (bool passthrough); public void set_pts_interpolation (bool pts_interpolate); [NoWrapper] public virtual bool set_sink_caps (Gst.Caps caps); public void set_syncable (bool syncable); [Version (since = "1.2")] public void set_ts_at_offset (size_t offset); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoAccessorMethod] public bool disable_passthrough { get; set; } } [CCode (cheader_filename = "gst/base/base.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_base_parse_frame_get_type ()")] [Compact] [GIR (name = "BaseParseFrame")] public class ParseFrame { public weak Gst.Buffer buffer; public uint flags; public uint64 offset; public weak Gst.Buffer out_buffer; public int overhead; [CCode (has_construct_function = false)] public ParseFrame (Gst.Buffer buffer, Gst.Base.ParseFrameFlags flags, int overhead); [Version (since = "1.12.1")] public Gst.Base.ParseFrame copy (); public void free (); public void init (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstPushSrc", lower_case_cprefix = "gst_push_src_", type_id = "gst_push_src_get_type ()")] [GIR (name = "PushSrc")] public class PushSrc : Gst.Base.Src { [CCode (has_construct_function = false)] protected PushSrc (); [NoWrapper] public virtual Gst.FlowReturn alloc (out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn create (out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn fill (Gst.Buffer buf); } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_sink_get_type ()")] [GIR (name = "BaseSink")] public abstract class Sink : Gst.Element { public bool can_activate_pull; public bool can_activate_push; public bool eos; public bool have_newsegment; public bool have_preroll; public bool need_preroll; public uint64 offset; public Gst.PadMode pad_mode; public bool playing_async; public GLib.Cond preroll_cond; public GLib.Mutex preroll_lock; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; [CCode (has_construct_function = false)] protected Sink (); [NoWrapper] public virtual bool activate_pull (bool active); public Gst.FlowReturn do_preroll (Gst.MiniObject obj); [NoWrapper] public virtual bool event (Gst.Event event); [NoWrapper] public virtual Gst.Caps fixate (Gst.Caps caps); public uint get_blocksize (); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Caps? filter); [Version (since = "1.12")] public bool get_drop_out_of_segment (); public Gst.Sample? get_last_sample (); public Gst.ClockTime get_latency (); [Version (since = "1.2")] public uint64 get_max_bitrate (); public int64 get_max_lateness (); [Version (since = "1.16")] public Gst.ClockTime get_processing_deadline (); public Gst.ClockTime get_render_delay (); [Version (since = "1.18")] public Gst.Structure get_stats (); public bool get_sync (); public uint64 get_throttle_time (); [NoWrapper] public virtual void get_times (Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end); public Gst.ClockTimeDiff get_ts_offset (); public bool is_async_enabled (); public bool is_last_sample_enabled (); public bool is_qos_enabled (); [NoWrapper] public virtual Gst.FlowReturn prepare (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn prepare_list (Gst.BufferList buffer_list); [NoWrapper] public virtual Gst.FlowReturn preroll (Gst.Buffer buffer); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); [NoWrapper] public virtual bool query (Gst.Query query); public bool query_latency (out bool live, out bool upstream_live, out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); [NoWrapper] public virtual Gst.FlowReturn render (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn render_list (Gst.BufferList buffer_list); public void set_async_enabled (bool enabled); public void set_blocksize (uint blocksize); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); [Version (since = "1.12")] public void set_drop_out_of_segment (bool drop_out_of_segment); public void set_last_sample_enabled (bool enabled); [Version (since = "1.2")] public void set_max_bitrate (uint64 max_bitrate); public void set_max_lateness (int64 max_lateness); [Version (since = "1.16")] public void set_processing_deadline (Gst.ClockTime processing_deadline); public void set_qos_enabled (bool enabled); public void set_render_delay (Gst.ClockTime delay); public void set_sync (bool sync); public void set_throttle_time (uint64 throttle); public void set_ts_offset (Gst.ClockTimeDiff offset); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool @unlock (); [NoWrapper] public virtual bool unlock_stop (); public Gst.FlowReturn wait (Gst.ClockTime time, out Gst.ClockTimeDiff jitter); public Gst.ClockReturn wait_clock (Gst.ClockTime time, out Gst.ClockTimeDiff jitter); [NoWrapper] public virtual Gst.FlowReturn wait_event (Gst.Event event); public Gst.FlowReturn wait_preroll (); [NoAccessorMethod] public bool @async { get; set; } public uint blocksize { get; set; } [NoAccessorMethod] public bool enable_last_sample { get; set; } public Gst.Sample last_sample { owned get; } [Version (since = "1.2")] public uint64 max_bitrate { get; set; } public int64 max_lateness { get; set; } [Version (since = "1.16")] public uint64 processing_deadline { get; set; } [NoAccessorMethod] public bool qos { get; set; } public uint64 render_delay { get; set; } [Version (since = "1.18")] public Gst.Structure stats { owned get; } public bool sync { get; set; } public uint64 throttle_time { get; set; } public int64 ts_offset { get; set; } } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_src_get_type ()")] [GIR (name = "BaseSrc")] public abstract class Src : Gst.Element { public bool can_activate_push; public Gst.ClockID clock_id; public GLib.Cond live_cond; public GLib.Mutex live_lock; public bool live_running; public bool need_newsegment; public int num_buffers_left; public weak Gst.Event pending_seek; public bool random_access; public bool running; public weak Gst.Segment segment; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Src (); [NoWrapper] public virtual Gst.FlowReturn alloc (uint64 offset, uint size, out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn create (uint64 offset, uint size, ref Gst.Buffer buf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual bool do_seek (Gst.Segment segment); [NoWrapper] public virtual bool event (Gst.Event event); [NoWrapper] public virtual Gst.FlowReturn fill (uint64 offset, uint size, Gst.Buffer buf); [NoWrapper] public virtual Gst.Caps fixate (Gst.Caps caps); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public uint get_blocksize (); public Gst.BufferPool? get_buffer_pool (); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Caps? filter); public bool get_do_timestamp (); [NoWrapper] public virtual bool get_size (out uint64 size); [NoWrapper] public virtual void get_times (Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end); public bool is_async (); [NoWrapper] public virtual bool is_seekable (); [Version (since = "1.18")] public virtual bool negotiate (); [Version (deprecated = true, deprecated_since = "1.18")] public bool new_seamless_segment (int64 start, int64 stop, int64 time); [Version (since = "1.18")] public bool new_segment (Gst.Segment segment); [NoWrapper] public virtual bool prepare_seek_segment (Gst.Event seek, Gst.Segment segment); [NoWrapper] public virtual bool query (Gst.Query query); public bool query_latency (out bool live, out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); public void set_async (bool @async); [Version (since = "1.4")] public void set_automatic_eos (bool automatic_eos); public void set_blocksize (uint blocksize); public virtual bool set_caps (Gst.Caps caps); public void set_do_timestamp (bool timestamp); public void set_dynamic_size (bool @dynamic); public void set_format (Gst.Format format); public void set_live (bool live); [NoWrapper] public virtual bool start (); public void start_complete (Gst.FlowReturn ret); public Gst.FlowReturn start_wait (); [NoWrapper] public virtual bool stop (); [Version (since = "1.14")] public void submit_buffer_list (owned Gst.BufferList buffer_list); [NoWrapper] public virtual bool @unlock (); [NoWrapper] public virtual bool unlock_stop (); public Gst.FlowReturn wait_playing (); public uint blocksize { get; set; } public bool do_timestamp { get; set; } [NoAccessorMethod] public int num_buffers { get; set; } [NoAccessorMethod] public bool typefind { get; set; } } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_transform_get_type ()")] [GIR (name = "BaseTransform")] public abstract class Transform : Gst.Element { public bool have_segment; public weak Gst.Buffer queued_buf; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Transform (); [NoWrapper] public virtual bool accept_caps (Gst.PadDirection direction, Gst.Caps caps); [NoWrapper] public virtual void before_transform (Gst.Buffer buffer); [NoWrapper] public virtual bool copy_metadata (Gst.Buffer input, Gst.Buffer outbuf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual bool filter_meta (Gst.Query query, GLib.Type api, Gst.Structure @params); [NoWrapper] public virtual Gst.Caps fixate_caps (Gst.PadDirection direction, Gst.Caps caps, owned Gst.Caps othercaps); [NoWrapper] public virtual Gst.FlowReturn generate_output (out Gst.Buffer outbuf); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public Gst.BufferPool? get_buffer_pool (); [NoWrapper] public virtual bool get_unit_size (Gst.Caps caps, out size_t size); public bool is_in_place (); public bool is_passthrough (); public bool is_qos_enabled (); [NoWrapper] public virtual Gst.FlowReturn prepare_output_buffer (Gst.Buffer input, out Gst.Buffer outbuf); [NoWrapper] public virtual bool propose_allocation (Gst.Query decide_query, Gst.Query query); [NoWrapper] public virtual bool query (Gst.PadDirection direction, Gst.Query query); [Version (since = "1.18")] public bool reconfigure (); public void reconfigure_sink (); public void reconfigure_src (); [NoWrapper] public virtual bool set_caps (Gst.Caps incaps, Gst.Caps outcaps); public void set_gap_aware (bool gap_aware); public void set_in_place (bool in_place); public void set_passthrough (bool passthrough); [Version (since = "1.0.1")] public void set_prefer_passthrough (bool prefer_passthrough); public void set_qos_enabled (bool enabled); [NoWrapper] public virtual bool sink_event (owned Gst.Event event); [NoWrapper] public virtual bool src_event (owned Gst.Event event); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual Gst.FlowReturn submit_input_buffer (bool is_discont, Gst.Buffer input); [NoWrapper] public virtual Gst.FlowReturn transform (Gst.Buffer inbuf, Gst.Buffer outbuf); [NoWrapper] public virtual Gst.Caps transform_caps (Gst.PadDirection direction, Gst.Caps caps, Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn transform_ip (Gst.Buffer buf); [NoWrapper] public virtual bool transform_meta (Gst.Buffer outbuf, Gst.Meta meta, Gst.Buffer inbuf); [NoWrapper] public virtual bool transform_size (Gst.PadDirection direction, Gst.Caps caps, size_t size, Gst.Caps othercaps, out size_t othersize); public void update_qos (double proportion, Gst.ClockTimeDiff diff, Gst.ClockTime timestamp); [Version (since = "1.6")] public bool update_src_caps (Gst.Caps updated_caps); [NoAccessorMethod] public bool qos { get; set; } } [CCode (cheader_filename = "gst/base/base.h", cname = "GstBitWriter", has_type_id = false)] [GIR (name = "BitWriter")] [Version (since = "1.16")] public struct BitWriter { public uint8 data; public uint bit_size; [CCode (cname = "gst_bit_writer_align_bytes")] public bool align_bytes (uint8 trailing_bit); [CCode (cname = "gst_bit_writer_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_bit_writer_free_and_get_buffer")] [DestroysInstance] public Gst.Buffer free_and_get_buffer (); [CCode (array_length = false, cname = "gst_bit_writer_free_and_get_data")] [DestroysInstance] public uint8[] free_and_get_data (); [CCode (array_length = false, cname = "gst_bit_writer_get_data")] public unowned uint8[] get_data (); [CCode (cname = "gst_bit_writer_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_bit_writer_get_size")] public uint get_size (); [CCode (cname = "gst_bit_writer_put_bits_uint16")] public bool put_bits_uint16 (uint16 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint32")] public bool put_bits_uint32 (uint32 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint64")] public bool put_bits_uint64 (uint64 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint8")] public bool put_bits_uint8 (uint8 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bytes")] public bool put_bytes ([CCode (array_length_cname = "nbytes", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_writer_reset")] public void reset (); [CCode (cname = "gst_bit_writer_reset_and_get_buffer")] public Gst.Buffer reset_and_get_buffer (); [CCode (array_length = false, cname = "gst_bit_writer_reset_and_get_data")] public uint8[] reset_and_get_data (); [CCode (cname = "gst_bit_writer_set_pos")] public bool set_pos (uint pos); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectData", has_type_id = false)] [GIR (name = "CollectData")] public struct CollectData { public weak Gst.Base.CollectPads collect; public weak Gst.Pad pad; public weak Gst.Buffer buffer; public uint pos; public weak Gst.Segment segment; [CCode (cname = "ABI.abi.dts")] public int64 ABI_abi_dts; } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregatorStartTimeSelection", cprefix = "GST_AGGREGATOR_START_TIME_SELECTION_", type_id = "gst_aggregator_start_time_selection_get_type ()")] [GIR (name = "AggregatorStartTimeSelection")] [Version (since = "1.18")] public enum AggregatorStartTimeSelection { ZERO, FIRST, SET } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsStateFlags", cprefix = "GST_COLLECT_PADS_STATE_", has_type_id = false)] [Flags] [GIR (name = "CollectPadsStateFlags")] public enum CollectPadsStateFlags { EOS, FLUSHING, NEW_SEGMENT, WAITING, LOCKED } [CCode (cheader_filename = "gst/base/base.h", cprefix = "GST_BASE_PARSE_FRAME_FLAG_", has_type_id = false)] [Flags] [GIR (name = "BaseParseFrameFlags")] public enum ParseFrameFlags { NONE, NEW_FRAME, NO_FRAME, CLIP, DROP, QUEUE } [CCode (cheader_filename = "gst/base/base.h", cprefix = "GST_BASE_SRC_FLAG_", has_type_id = false)] [Flags] [GIR (name = "BaseSrcFlags")] public enum SrcFlags { STARTING, STARTED, LAST } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectDataDestroyNotify", has_target = false)] public delegate void CollectDataDestroyNotify (Gst.Base.CollectData data); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsBufferFunction", instance_pos = 3.9)] public delegate Gst.FlowReturn CollectPadsBufferFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data, owned Gst.Buffer buffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsClipFunction", instance_pos = 4.9)] public delegate Gst.FlowReturn CollectPadsClipFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data, owned Gst.Buffer inbuffer, out Gst.Buffer outbuffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsCompareFunction", instance_pos = 5.9)] public delegate int CollectPadsCompareFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data1, Gst.ClockTime timestamp1, Gst.Base.CollectData data2, Gst.ClockTime timestamp2); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsEventFunction", instance_pos = 3.9)] public delegate bool CollectPadsEventFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData pad, Gst.Event event); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsFlushFunction", instance_pos = 1.9)] [Version (since = "1.4")] public delegate void CollectPadsFlushFunction (Gst.Base.CollectPads pads); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsFunction", instance_pos = 1.9)] public delegate Gst.FlowReturn CollectPadsFunction (Gst.Base.CollectPads pads); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsQueryFunction", instance_pos = 3.9)] public delegate bool CollectPadsQueryFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData pad, Gst.Query query); [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueueEmptyCallback", has_target = false)] public delegate void DataQueueEmptyCallback (Gst.Base.DataQueue queue, void* checkdata); [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueueFullCallback", has_target = false)] public delegate void DataQueueFullCallback (Gst.Base.DataQueue queue, void* checkdata); [CCode (cheader_filename = "gst/base/base.h", cname = "GstTypeFindHelperGetRangeFunction", has_target = false)] public delegate Gst.FlowReturn TypeFindHelperGetRangeFunction (Gst.Object obj, Gst.Object? parent, uint64 offset, uint length, out Gst.Buffer buffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_PARSE_FLAG_DRAINING")] public const int PARSE_FLAG_DRAINING; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_PARSE_FLAG_LOST_SYNC")] public const int PARSE_FLAG_LOST_SYNC; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_TRANSFORM_SINK_NAME")] public const string TRANSFORM_SINK_NAME; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_TRANSFORM_SRC_NAME")] public const string TRANSFORM_SRC_NAME; [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper")] public static Gst.Caps? type_find_helper (Gst.Pad src, uint64 size); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_buffer")] public static Gst.Caps? type_find_helper_for_buffer (Gst.Object? obj, Gst.Buffer buf, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_buffer_with_extension")] [Version (since = "1.16")] public static Gst.Caps? type_find_helper_for_buffer_with_extension (Gst.Object? obj, Gst.Buffer buf, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_data")] public static Gst.Caps? type_find_helper_for_data (Gst.Object? obj, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_data_with_extension")] [Version (since = "1.16")] public static Gst.Caps? type_find_helper_for_data_with_extension (Gst.Object? obj, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_extension")] public static Gst.Caps? type_find_helper_for_extension (Gst.Object? obj, string extension); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_get_range")] public static Gst.Caps? type_find_helper_get_range (Gst.Object obj, Gst.Object? parent, Gst.Base.TypeFindHelperGetRangeFunction func, uint64 size, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_get_range_full")] [Version (since = "1.14.3")] public static Gst.FlowReturn type_find_helper_get_range_full (Gst.Object obj, Gst.Object? parent, Gst.Base.TypeFindHelperGetRangeFunction func, uint64 size, string? extension, out Gst.Caps caps, out Gst.TypeFindProbability prob); } } dino-0.4.3/plugins/rtp/vapi/gstreamer-rtp-1.0.vapi0000644000000000000000000010335414452563620020360 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-rtp-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstRtp", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace RTCP { [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTCPBuffer")] public struct Buffer { public weak Gst.Buffer buffer; public bool add_packet (Gst.RTCP.Type type, Gst.RTCP.Packet packet); public bool get_first_packet (Gst.RTCP.Packet packet); public uint get_packet_count (); public static bool map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTCP.Buffer rtcp); public static Gst.Buffer @new (uint mtu); public static Gst.Buffer new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); public static Gst.Buffer new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] owned uint8[] data); public bool unmap (); public static bool validate (Gst.Buffer buffer); public static bool validate_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [Version (since = "1.6")] public static bool validate_data_reduced ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [Version (since = "1.6")] public static bool validate_reduced (Gst.Buffer buffer); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTCPPacket")] public struct Packet { public weak Gst.RTCP.Buffer? rtcp; public uint offset; [Version (since = "1.10")] public bool add_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); public bool add_rb (uint32 ssrc, uint8 fractionlost, int32 packetslost, uint32 exthighestseq, uint32 jitter, uint32 lsr, uint32 dlsr); [Version (since = "1.10")] public uint8 app_get_data (); [Version (since = "1.10")] public uint16 app_get_data_length (); [Version (since = "1.10")] public unowned string app_get_name (); [Version (since = "1.10")] public uint32 app_get_ssrc (); [Version (since = "1.10")] public uint8 app_get_subtype (); [Version (since = "1.10")] public bool app_set_data_length (uint16 wordlen); [Version (since = "1.10")] public void app_set_name (string name); [Version (since = "1.10")] public void app_set_ssrc (uint32 ssrc); [Version (since = "1.10")] public void app_set_subtype (uint8 subtype); public bool bye_add_ssrc (uint32 ssrc); public bool bye_add_ssrcs ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint32[] ssrc); public uint32 bye_get_nth_ssrc (uint nth); public string bye_get_reason (); public uint8 bye_get_reason_len (); public uint bye_get_ssrc_count (); public bool bye_set_reason (string reason); [Version (since = "1.10")] public bool copy_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] out uint8[] data); public uint8 fb_get_fci (); public uint16 fb_get_fci_length (); public uint32 fb_get_media_ssrc (); public uint32 fb_get_sender_ssrc (); public Gst.RTCP.FBType fb_get_type (); public bool fb_set_fci_length (uint16 wordlen); public void fb_set_media_ssrc (uint32 ssrc); public void fb_set_sender_ssrc (uint32 ssrc); public void fb_set_type (Gst.RTCP.FBType type); public uint8 get_count (); public uint16 get_length (); public bool get_padding (); [Version (since = "1.10")] public bool get_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] out unowned uint8[] data); [Version (since = "1.10")] public uint16 get_profile_specific_ext_length (); public void get_rb (uint nth, out uint32 ssrc, out uint8 fractionlost, out int32 packetslost, out uint32 exthighestseq, out uint32 jitter, out uint32 lsr, out uint32 dlsr); public uint get_rb_count (); public Gst.RTCP.Type get_type (); public bool move_to_next (); public bool remove (); public uint32 rr_get_ssrc (); public void rr_set_ssrc (uint32 ssrc); public bool sdes_add_entry (Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] uint8[] data); public bool sdes_add_item (uint32 ssrc); public bool sdes_copy_entry (out Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] out uint8[] data); public bool sdes_first_entry (); public bool sdes_first_item (); public bool sdes_get_entry (out Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] out unowned uint8[] data); public uint sdes_get_item_count (); public uint32 sdes_get_ssrc (); public bool sdes_next_entry (); public bool sdes_next_item (); public void set_rb (uint nth, uint32 ssrc, uint8 fractionlost, int32 packetslost, uint32 exthighestseq, uint32 jitter, uint32 lsr, uint32 dlsr); public void sr_get_sender_info (out uint32 ssrc, out uint64 ntptime, out uint32 rtptime, out uint32 packet_count, out uint32 octet_count); public void sr_set_sender_info (uint32 ssrc, uint64 ntptime, uint32 rtptime, uint32 packet_count, uint32 octet_count); [Version (since = "1.16")] public bool xr_first_rb (); [Version (since = "1.16")] public uint16 xr_get_block_length (); [Version (since = "1.16")] public Gst.RTCP.XRType xr_get_block_type (); [Version (since = "1.16")] public bool xr_get_dlrr_block (uint nth, out uint32 ssrc, out uint32 last_rr, out uint32 delay); [Version (since = "1.16")] public bool xr_get_prt_by_seq (uint16 seq, out uint32 receipt_time); [Version (since = "1.16")] public bool xr_get_prt_info (out uint32 ssrc, out uint8 thinning, out uint16 begin_seq, out uint16 end_seq); [Version (since = "1.16")] public bool xr_get_rle_info (out uint32 ssrc, out uint8 thinning, out uint16 begin_seq, out uint16 end_seq, out uint32 chunk_count); [Version (since = "1.16")] public bool xr_get_rle_nth_chunk (uint nth, out uint16 chunk); [Version (since = "1.16")] public bool xr_get_rrt (out uint64 timestamp); [Version (since = "1.16")] public uint32 xr_get_ssrc (); [Version (since = "1.16")] public bool xr_get_summary_info (out uint32 ssrc, out uint16 begin_seq, out uint16 end_seq); [Version (since = "1.16")] public bool xr_get_summary_jitter (out uint32 min_jitter, out uint32 max_jitter, out uint32 mean_jitter, out uint32 dev_jitter); [Version (since = "1.16")] public bool xr_get_summary_pkt (out uint32 lost_packets, out uint32 dup_packets); [Version (since = "1.16")] public bool xr_get_summary_ttl (out bool is_ipv4, out uint8 min_ttl, out uint8 max_ttl, out uint8 mean_ttl, out uint8 dev_ttl); [Version (since = "1.16")] public bool xr_get_voip_burst_metrics (out uint8 burst_density, out uint8 gap_density, out uint16 burst_duration, out uint16 gap_duration); [Version (since = "1.16")] public bool xr_get_voip_configuration_params (out uint8 gmin, out uint8 rx_config); [Version (since = "1.16")] public bool xr_get_voip_delay_metrics (out uint16 roundtrip_delay, out uint16 end_system_delay); [Version (since = "1.16")] public bool xr_get_voip_jitter_buffer_params (out uint16 jb_nominal, out uint16 jb_maximum, out uint16 jb_abs_max); [Version (since = "1.16")] public bool xr_get_voip_metrics_ssrc (out uint32 ssrc); [Version (since = "1.16")] public bool xr_get_voip_packet_metrics (out uint8 loss_rate, out uint8 discard_rate); [Version (since = "1.16")] public bool xr_get_voip_quality_metrics (out uint8 r_factor, out uint8 ext_r_factor, out uint8 mos_lq, out uint8 mos_cq); [Version (since = "1.16")] public bool xr_get_voip_signal_metrics (out uint8 signal_level, out uint8 noise_level, out uint8 rerl, out uint8 gmin); [Version (since = "1.16")] public bool xr_next_rb (); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_", type_id = "gst_rtcpfb_type_get_type ()")] [GIR (name = "RTCPFBType")] public enum FBType { FB_TYPE_INVALID, RTPFB_TYPE_NACK, RTPFB_TYPE_TMMBR, RTPFB_TYPE_TMMBN, RTPFB_TYPE_RTCP_SR_REQ, RTPFB_TYPE_TWCC, PSFB_TYPE_PLI, PSFB_TYPE_SLI, PSFB_TYPE_RPSI, PSFB_TYPE_AFB, PSFB_TYPE_FIR, PSFB_TYPE_TSTR, PSFB_TYPE_TSTN, PSFB_TYPE_VBCN } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_SDES_", type_id = "gst_rtcpsdes_type_get_type ()")] [GIR (name = "RTCPSDESType")] public enum SDESType { INVALID, END, CNAME, NAME, EMAIL, PHONE, LOC, TOOL, NOTE, PRIV, [Version (since = "1.20")] H323_CADDR, [Version (since = "1.20")] APSI, [Version (since = "1.20")] RGRP, [Version (since = "1.20")] RTP_STREAM_ID, [Version (since = "1.20")] REPAIRED_RTP_STREAM_ID, [Version (since = "1.20")] CCID, [Version (since = "1.20")] MID; [CCode (cname = "gst_rtcp_sdes_name_to_type")] public static Gst.RTCP.SDESType from_string (string name); [CCode (cname = "gst_rtcp_sdes_type_to_name")] public unowned string to_string (); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_TYPE_", type_id = "gst_rtcp_type_get_type ()")] [GIR (name = "RTCPType")] public enum Type { INVALID, SR, RR, SDES, BYE, APP, RTPFB, PSFB, XR } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_XR_TYPE_", type_id = "gst_rtcpxr_type_get_type ()")] [GIR (name = "RTCPXRType")] [Version (since = "1.16")] public enum XRType { INVALID, LRLE, DRLE, PRT, RRT, DLRR, SSUMM, VOIP_METRICS } [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_BYE_SSRC_COUNT")] public const int MAX_BYE_SSRC_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_RB_COUNT")] public const int MAX_RB_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_SDES")] public const int MAX_SDES; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_SDES_ITEM_COUNT")] public const int MAX_SDES_ITEM_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_REDUCED_SIZE_VALID_MASK")] public const int REDUCED_SIZE_VALID_MASK; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VALID_MASK")] public const int VALID_MASK; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VALID_VALUE")] public const int VALID_VALUE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VERSION")] public const int VERSION; [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.map")] public static bool buffer_map (Gst.Buffer buffer, Gst.MapFlags flags, Gst.RTCP.Buffer rtcp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new")] public static Gst.Buffer buffer_new (uint mtu); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new_copy_data")] public static Gst.Buffer buffer_new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new_take_data")] public static Gst.Buffer buffer_new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate")] public static bool buffer_validate (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_data")] public static bool buffer_validate_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_data_reduced", since = "1.6")] public static bool buffer_validate_data_reduced ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_reduced", since = "1.6")] public static bool buffer_validate_reduced (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static uint64 ntp_to_unix (uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static uint64 unix_to_ntp (uint64 unixtime); } namespace RTP { [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_audio_payload_get_type ()")] [GIR (name = "RTPBaseAudioPayload")] public class BaseAudioPayload : Gst.RTP.BasePayload { public Gst.ClockTime base_ts; public int frame_duration; public int frame_size; public int sample_size; [CCode (has_construct_function = false)] protected BaseAudioPayload (); public Gst.FlowReturn flush (uint payload_len, Gst.ClockTime timestamp); public Gst.Base.Adapter get_adapter (); public Gst.FlowReturn push ([CCode (array_length_cname = "payload_len", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, Gst.ClockTime timestamp); public void set_frame_based (); public void set_frame_options (int frame_duration, int frame_size); public void set_sample_based (); public void set_sample_options (int sample_size); public void set_samplebits_options (int sample_size); [NoAccessorMethod] public bool buffer_list { get; set; } } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_depayload_get_type ()")] [GIR (name = "RTPBaseDepayload")] public abstract class BaseDepayload : Gst.Element { public uint clock_rate; public bool need_newsegment; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected BaseDepayload (); [NoWrapper] public virtual bool handle_event (Gst.Event event); [Version (since = "1.16")] public bool is_source_info_enabled (); [NoWrapper] public virtual bool packet_lost (Gst.Event event); [NoWrapper] public virtual Gst.Buffer process (Gst.Buffer @in); [NoWrapper] public virtual Gst.Buffer process_rtp_packet (Gst.RTP.Buffer rtp_buffer); public Gst.FlowReturn push (Gst.Buffer out_buf); public Gst.FlowReturn push_list (Gst.BufferList out_list); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); [Version (since = "1.16")] public void set_source_info_enabled (bool enable); [NoAccessorMethod] [Version (since = "1.20")] public bool auto_header_extension { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public int max_reorder { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool source_info { get; set; } [NoAccessorMethod] public Gst.Structure stats { owned get; } [Version (since = "1.20")] public signal void add_extension (owned Gst.RTP.HeaderExtension ext); [Version (since = "1.20")] public signal void clear_extensions (); [Version (since = "1.20")] public signal Gst.RTP.HeaderExtension request_extension (uint ext_id, string? ext_uri); } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_payload_get_type ()")] [GIR (name = "RTPBasePayload")] public abstract class BasePayload : Gst.Element { [CCode (has_construct_function = false)] protected BasePayload (); [Version (since = "1.16")] public Gst.Buffer allocate_output_buffer (uint payload_len, uint8 pad_len, uint8 csrc_count); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Pad pad, Gst.Caps filter); [Version (since = "1.16")] public uint get_source_count (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn handle_buffer (Gst.Buffer buffer); public bool is_filled (uint size, Gst.ClockTime duration); [Version (since = "1.16")] public bool is_source_info_enabled (); public Gst.FlowReturn push (owned Gst.Buffer buffer); public Gst.FlowReturn push_list (owned Gst.BufferList list); [NoWrapper] public virtual bool query (Gst.Pad pad, Gst.Query query); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); public void set_options (string media, bool @dynamic, string encoding_name, uint32 clock_rate); [Version (since = "1.20")] public bool set_outcaps_structure (Gst.Structure? s); [Version (since = "1.16")] public void set_source_info_enabled (bool enable); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoAccessorMethod] [Version (since = "1.20")] public bool auto_header_extension { get; set; } [NoAccessorMethod] public int64 max_ptime { get; set; } [NoAccessorMethod] public int64 min_ptime { get; set; } [NoAccessorMethod] public uint mtu { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool onvif_no_rate_control { get; set; } [NoAccessorMethod] public bool perfect_rtptime { get; set; } [NoAccessorMethod] public uint pt { get; set; } [NoAccessorMethod] public int64 ptime_multiple { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public bool scale_rtptime { get; set; } [NoAccessorMethod] public uint seqnum { get; } [NoAccessorMethod] public int seqnum_offset { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool source_info { get; set; } [NoAccessorMethod] public uint ssrc { get; set; } [NoAccessorMethod] public Gst.Structure stats { owned get; } [NoAccessorMethod] public uint timestamp { get; } [NoAccessorMethod] public uint timestamp_offset { get; set; } [Version (since = "1.20")] public signal void add_extension (owned Gst.RTP.HeaderExtension ext); [Version (since = "1.20")] public signal void clear_extensions (); [Version (since = "1.20")] public signal Gst.RTP.HeaderExtension request_extension (uint ext_id, string ext_uri); } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_header_extension_get_type ()")] [GIR (name = "RTPHeaderExtension")] [Version (since = "1.20")] public abstract class HeaderExtension : Gst.Element { [CCode (has_construct_function = false)] protected HeaderExtension (); public static Gst.RTP.HeaderExtension? create_from_uri (string uri); public Gst.RTP.HeaderExtensionDirection get_direction (); public uint get_id (); public virtual size_t get_max_size (Gst.Buffer input_meta); public string get_sdp_caps_field_name (); public virtual Gst.RTP.HeaderExtensionFlags get_supported_flags (); public unowned string get_uri (); public virtual bool read (Gst.RTP.HeaderExtensionFlags read_flags, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, Gst.Buffer buffer); [NoWrapper] public virtual bool set_attributes (Gst.RTP.HeaderExtensionDirection direction, string attributes); public bool set_attributes_from_caps (Gst.Caps caps); public virtual bool set_caps_from_attributes (Gst.Caps caps); public bool set_caps_from_attributes_helper (Gst.Caps caps, string attributes); public void set_direction (Gst.RTP.HeaderExtensionDirection direction); public void set_id (uint ext_id); public virtual bool set_non_rtp_sink_caps (Gst.Caps caps); [CCode (cname = "gst_rtp_header_extension_class_set_uri")] public class void set_uri (string uri); public void set_wants_update_non_rtp_src_caps (bool state); public virtual bool update_non_rtp_src_caps (Gst.Caps caps); public bool wants_update_non_rtp_src_caps (); public virtual ssize_t write (Gst.Buffer input_meta, Gst.RTP.HeaderExtensionFlags write_flags, Gst.Buffer output, [CCode (array_length_cname = "size", array_length_pos = 4.1, array_length_type = "gsize")] uint8[] data); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPBuffer")] public struct Buffer { public weak Gst.Buffer buffer; public uint state; [CCode (array_length = false)] public weak void* data[4]; [CCode (array_length = false)] public weak size_t size[4]; public bool add_extension_onebyte_header (uint8 id, [CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "guint")] uint8[] data); public bool add_extension_twobytes_header (uint8 appbits, uint8 id, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "guint")] uint8[] data); public static void allocate_data (Gst.Buffer buffer, uint payload_len, uint8 pad_len, uint8 csrc_count); public static uint calc_header_len (uint8 csrc_count); public static uint calc_packet_len (uint payload_len, uint8 pad_len, uint8 csrc_count); public static uint calc_payload_len (uint packet_len, uint8 pad_len, uint8 csrc_count); public static int compare_seqnum (uint16 seqnum1, uint16 seqnum2); public static uint32 default_clock_rate (uint8 payload_type); public static uint64 ext_timestamp (ref uint64 exttimestamp, uint32 timestamp); public uint32 get_csrc (uint8 idx); public uint8 get_csrc_count (); public bool get_extension (); [Version (since = "1.2")] public GLib.Bytes get_extension_bytes (out uint16 bits); public bool get_extension_data (out uint16 bits, [CCode (array_length = false)] out unowned uint8[] data, out uint wordlen); public bool get_extension_onebyte_header (uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "guint")] out unowned uint8[] data); [Version (since = "1.18")] public static bool get_extension_onebyte_header_from_bytes (GLib.Bytes bytes, uint16 bit_pattern, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 5.1, array_length_type = "guint")] out unowned uint8[] data); public bool get_extension_twobytes_header (out uint8 appbits, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 4.1, array_length_type = "guint")] out unowned uint8[] data); public uint get_header_len (); public bool get_marker (); public uint get_packet_len (); public bool get_padding (); [CCode (array_length = false)] public unowned uint8[] get_payload (); public Gst.Buffer get_payload_buffer (); [Version (since = "1.2")] public GLib.Bytes get_payload_bytes (); public uint get_payload_len (); public Gst.Buffer get_payload_subbuffer (uint offset, uint len); public uint8 get_payload_type (); public uint16 get_seq (); public uint32 get_ssrc (); public uint32 get_timestamp (); public uint8 get_version (); public static bool map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTP.Buffer rtp); public static Gst.Buffer new_allocate (uint payload_len, uint8 pad_len, uint8 csrc_count); public static Gst.Buffer new_allocate_len (uint packet_len, uint8 pad_len, uint8 csrc_count); public static Gst.Buffer new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] uint8[] data); public static Gst.Buffer new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] owned uint8[] data); public void pad_to (uint len); [Version (since = "1.20")] public void remove_extension_data (); public void set_csrc (uint8 idx, uint32 csrc); public void set_extension (bool extension); public bool set_extension_data (uint16 bits, uint16 length); public void set_marker (bool marker); public void set_packet_len (uint len); public void set_padding (bool padding); public void set_payload_type (uint8 payload_type); public void set_seq (uint16 seq); public void set_ssrc (uint32 ssrc); public void set_timestamp (uint32 timestamp); public void set_version (uint8 version); public void unmap (); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPPayloadInfo")] public struct PayloadInfo { public uint8 payload_type; public weak string media; public weak string encoding_name; public uint clock_rate; public weak string encoding_parameters; public uint bitrate; public static unowned Gst.RTP.PayloadInfo? for_name (string media, string encoding_name); public static unowned Gst.RTP.PayloadInfo? for_pt (uint8 payload_type); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPSourceMeta")] [Version (since = "1.16")] public struct SourceMeta { public Gst.Meta meta; public uint32 ssrc; public bool ssrc_valid; [CCode (array_length = false)] public weak uint32 csrc[15]; public uint csrc_count; public bool append_csrc ([CCode (array_length_cname = "csrc_count", array_length_pos = 1.1, array_length_type = "guint", type = "const guint32*")] uint32[] csrc); public static unowned Gst.MetaInfo? get_info (); public uint get_source_count (); public bool set_ssrc (uint32? ssrc); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_BUFFER_FLAG_", type_id = "gst_rtp_buffer_flags_get_type ()")] [Flags] [GIR (name = "RTPBufferFlags")] [Version (since = "1.10")] public enum BufferFlags { RETRANSMISSION, REDUNDANT, LAST } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_BUFFER_MAP_FLAG_", type_id = "gst_rtp_buffer_map_flags_get_type ()")] [Flags] [GIR (name = "RTPBufferMapFlags")] [Version (since = "1.6.1")] public enum BufferMapFlags { SKIP_PADDING, LAST } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_HEADER_EXTENSION_DIRECTION_", type_id = "gst_rtp_header_extension_direction_get_type ()")] [Flags] [GIR (name = "RTPHeaderExtensionDirection")] [Version (since = "1.20")] public enum HeaderExtensionDirection { INACTIVE, SENDONLY, RECVONLY, SENDRECV, INHERITED } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_HEADER_EXTENSION_", type_id = "gst_rtp_header_extension_flags_get_type ()")] [Flags] [GIR (name = "RTPHeaderExtensionFlags")] [Version (since = "1.20")] public enum HeaderExtensionFlags { ONE_BYTE, TWO_BYTE } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_PAYLOAD_", type_id = "gst_rtp_payload_get_type ()")] [GIR (name = "RTPPayload")] public enum Payload { PCMU, @1016, G721, GSM, G723, DVI4_8000, DVI4_16000, LPC, PCMA, G722, L16_STEREO, L16_MONO, QCELP, CN, MPA, G728, DVI4_11025, DVI4_22050, G729, CELLB, JPEG, NV, H261, MPV, MP2T, H263; public const string @1016_STRING; public const string CELLB_STRING; public const string CN_STRING; public const string DVI4_11025_STRING; public const string DVI4_16000_STRING; public const string DVI4_22050_STRING; public const string DVI4_8000_STRING; public const string DYNAMIC_STRING; public const string G721_STRING; public const string G722_STRING; public const int G723_53; public const string G723_53_STRING; public const int G723_63; public const string G723_63_STRING; public const string G723_STRING; public const string G728_STRING; public const string G729_STRING; public const string GSM_STRING; public const string H261_STRING; public const string H263_STRING; public const string JPEG_STRING; public const string L16_MONO_STRING; public const string L16_STEREO_STRING; public const string LPC_STRING; public const string MP2T_STRING; public const string MPA_STRING; public const string MPV_STRING; public const string NV_STRING; public const string PCMA_STRING; public const string PCMU_STRING; public const string QCELP_STRING; public const int TS41; public const string TS41_STRING; public const int TS48; public const string TS48_STRING; } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_PROFILE_", type_id = "gst_rtp_profile_get_type ()")] [GIR (name = "RTPProfile")] [Version (since = "1.6")] public enum Profile { UNKNOWN, AVP, SAVP, AVPF, SAVPF } [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_BASE")] public const string HDREXT_BASE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_ELEMENT_CLASS")] [Version (since = "1.20")] public const string HDREXT_ELEMENT_CLASS; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_56")] public const string HDREXT_NTP_56; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_56_SIZE")] public const int HDREXT_NTP_56_SIZE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_64")] public const string HDREXT_NTP_64; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_64_SIZE")] public const int HDREXT_NTP_64_SIZE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY")] [Version (since = "1.20")] public const string HEADER_EXTENSION_URI_METADATA_KEY; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_SOURCE_META_MAX_CSRC_COUNT")] public const int SOURCE_META_MAX_CSRC_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_VERSION")] public const int VERSION; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "gst_buffer_add_rtp_source_meta")] [Version (since = "1.16")] public static unowned Gst.RTP.SourceMeta? buffer_add_rtp_source_meta (Gst.Buffer buffer, uint32? ssrc, uint32? csrc, uint csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.allocate_data")] public static void buffer_allocate_data (Gst.Buffer buffer, uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_header_len")] public static uint buffer_calc_header_len (uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_packet_len")] public static uint buffer_calc_packet_len (uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_payload_len")] public static uint buffer_calc_payload_len (uint packet_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.compare_seqnum")] public static int buffer_compare_seqnum (uint16 seqnum1, uint16 seqnum2); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.default_clock_rate")] public static uint32 buffer_default_clock_rate (uint8 payload_type); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.ext_timestamp")] public static uint64 buffer_ext_timestamp (ref uint64 exttimestamp, uint32 timestamp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.get_extension_onebyte_header_from_bytes", since = "1.18")] public static bool buffer_get_extension_onebyte_header_from_bytes (GLib.Bytes bytes, uint16 bit_pattern, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 5.1, array_length_type = "guint")] out unowned uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "gst_buffer_get_rtp_source_meta")] [Version (since = "1.16")] public static unowned Gst.RTP.SourceMeta? buffer_get_rtp_source_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.map")] public static bool buffer_map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTP.Buffer rtp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_allocate")] public static Gst.Buffer buffer_new_allocate (uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_allocate_len")] public static Gst.Buffer buffer_new_allocate_len (uint packet_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_copy_data")] public static Gst.Buffer buffer_new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_take_data")] public static Gst.Buffer buffer_new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] owned uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (since = "1.20")] public static GLib.List get_header_extension_list (); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_get_ntp_56 ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, out uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_get_ntp_64 ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, out uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_set_ntp_56 (void* data, uint size, uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_set_ntp_64 (void* data, uint size, uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPPayloadInfo.for_name")] public static unowned Gst.RTP.PayloadInfo? payload_info_for_name (string media, string encoding_name); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPPayloadInfo.for_pt")] public static unowned Gst.RTP.PayloadInfo? payload_info_for_pt (uint8 payload_type); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static GLib.Type source_meta_api_get_type (); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPSourceMeta.get_info")] public static unowned Gst.MetaInfo? source_meta_get_info (); } } dino-0.4.3/plugins/rtp/vapi/gstreamer-video-1.0.vapi0000644000000000000000000033673714452563620020676 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-video-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstVideo", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace Video { [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_get_type ()")] [GIR (name = "VideoAggregator")] [Version (since = "1.16")] public abstract class Aggregator : Gst.Base.Aggregator { public weak Gst.Video.Info info; [CCode (has_construct_function = false)] protected Aggregator (); [NoWrapper] public virtual Gst.FlowReturn aggregate_frames (Gst.Buffer outbuffer); [NoWrapper] public virtual Gst.FlowReturn create_output_buffer (Gst.Buffer outbuffer); [NoWrapper] public virtual void find_best_format (Gst.Caps downstream_caps, Gst.Video.Info best_info, out bool at_least_one_alpha); [Version (since = "1.20")] public Gst.TaskPool get_execution_task_pool (); [NoWrapper] public virtual Gst.Caps update_caps (Gst.Caps caps); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_convert_pad_get_type ()")] [GIR (name = "VideoAggregatorConvertPad")] [Version (since = "1.16")] public class AggregatorConvertPad : Gst.Video.AggregatorPad { [CCode (has_construct_function = false)] protected AggregatorConvertPad (); [NoWrapper] public virtual void create_conversion_info (Gst.Video.Aggregator agg, Gst.Video.Info conversion_info); public void update_conversion_info (); [NoAccessorMethod] public Gst.Structure converter_config { owned get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_pad_get_type ()")] [GIR (name = "VideoAggregatorPad")] [Version (since = "1.16")] public class AggregatorPad : Gst.Base.AggregatorPad { public weak Gst.Video.Info info; [CCode (has_construct_function = false)] protected AggregatorPad (); [NoWrapper] public virtual void clean_frame (Gst.Video.Aggregator videoaggregator, Gst.Video.Frame prepared_frame); public unowned Gst.Buffer get_current_buffer (); public unowned Gst.Video.Frame? get_prepared_frame (); public bool has_current_buffer (); [NoWrapper] public virtual bool prepare_frame (Gst.Video.Aggregator videoaggregator, Gst.Buffer buffer, Gst.Video.Frame prepared_frame); [NoWrapper] [Version (since = "1.20")] public virtual void prepare_frame_finish (Gst.Video.Aggregator videoaggregator, Gst.Video.Frame prepared_frame); [NoWrapper] [Version (since = "1.20")] public virtual void prepare_frame_start (Gst.Video.Aggregator videoaggregator, Gst.Buffer buffer, Gst.Video.Frame prepared_frame); public void set_needs_alpha (bool needs_alpha); [NoWrapper] public virtual void update_conversion_info (); [NoAccessorMethod] public uint64 max_last_buffer_repeat { get; set; } [NoAccessorMethod] public bool repeat_after_eos { get; set; } [NoAccessorMethod] public uint zorder { get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_parallel_convert_pad_get_type ()")] [GIR (name = "VideoAggregatorParallelConvertPad")] [Version (since = "1.20")] public class AggregatorParallelConvertPad : Gst.Video.AggregatorConvertPad { [CCode (has_construct_function = false)] protected AggregatorParallelConvertPad (); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_buffer_pool_get_type ()")] [GIR (name = "VideoBufferPool")] public class BufferPool : Gst.BufferPool { [CCode (has_construct_function = false, type = "GstBufferPool*")] public BufferPool (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoChromaResample")] public class ChromaResample { [CCode (cname = "gst_video_chroma_resample_new", has_construct_function = false)] public ChromaResample (Gst.Video.ChromaMethod method, Gst.Video.ChromaSite site, Gst.Video.ChromaFlags flags, Gst.Video.Format format, int h_factor, int v_factor); public void free (); public void get_info (out uint n_lines, out int offset); [CCode (cname = "gst_video_chroma_resample")] public void resample (void* lines, int width); } [CCode (cheader_filename = "gst/video/gstvideoutils.h", ref_function = "gst_video_codec_frame_ref", type_id = "gst_video_codec_frame_get_type ()", unref_function = "gst_video_codec_frame_unref")] [Compact] [GIR (name = "VideoCodecFrame")] public class CodecFrame { public Gst.ClockTime deadline; public int distance_from_sync; public Gst.ClockTime dts; public Gst.ClockTime duration; public weak Gst.Buffer input_buffer; public weak Gst.Buffer output_buffer; public Gst.ClockTime pts; public uint32 system_frame_number; [CCode (simple_generics = true)] public T get_user_data (); public unowned Gst.Video.CodecFrame @ref (); [CCode (simple_generics = true)] public void set_user_data (owned T user_data); public void unref (); } [CCode (cheader_filename = "gst/video/video.h", ref_function = "gst_video_codec_state_ref", type_id = "gst_video_codec_state_get_type ()", unref_function = "gst_video_codec_state_unref")] [Compact] [GIR (name = "VideoCodecState")] public class CodecState { public weak Gst.Caps allocation_caps; public weak Gst.Caps caps; public weak Gst.Buffer codec_data; [Version (since = "1.20")] public Gst.Video.ContentLightLevel content_light_level; public weak Gst.Video.Info info; [Version (since = "1.20")] public Gst.Video.MasteringDisplayInfo mastering_display_info; public unowned Gst.Video.CodecState @ref (); public void unref (); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalanceChannel", lower_case_cprefix = "gst_color_balance_channel_", type_id = "gst_color_balance_channel_get_type ()")] [GIR (name = "ColorBalanceChannel")] public class ColorBalanceChannel : GLib.Object { public weak string label; public int max_value; public int min_value; [CCode (has_construct_function = false)] protected ColorBalanceChannel (); public virtual signal void value_changed (int value); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoConverter")] public class Converter { [Version (since = "1.6")] public void frame (Gst.Video.Frame src, Gst.Video.Frame dest); [Version (since = "1.20")] public void frame_finish (); [Version (since = "1.6")] public void free (); public unowned Gst.Structure get_config (); [Version (since = "1.6")] public bool set_config (owned Gst.Structure config); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_decoder_get_type ()")] [GIR (name = "VideoDecoder")] public abstract class Decoder : Gst.Element { [CCode (has_construct_function = false)] protected Decoder (); public void add_to_frame (int n_bytes); public Gst.Buffer allocate_output_buffer (); public Gst.FlowReturn allocate_output_frame (Gst.Video.CodecFrame frame); [Version (since = "1.12")] public Gst.FlowReturn allocate_output_frame_with_params (Gst.Video.CodecFrame frame, Gst.BufferPoolAcquireParams @params); [NoWrapper] public virtual bool close (); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual Gst.FlowReturn drain (); public Gst.FlowReturn drop_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public Gst.FlowReturn drop_subframe (owned Gst.Video.CodecFrame frame); [NoWrapper] public virtual Gst.FlowReturn finish (); public Gst.FlowReturn finish_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public Gst.FlowReturn finish_subframe (owned Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool flush (); public void get_allocator (out Gst.Allocator allocator, out Gst.AllocationParams @params); public Gst.BufferPool get_buffer_pool (); public int get_estimate_rate (); public Gst.Video.CodecFrame get_frame (int frame_number); public GLib.List get_frames (); [Version (since = "1.20")] public uint get_input_subframe_index (Gst.Video.CodecFrame frame); public void get_latency (out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); public Gst.ClockTimeDiff get_max_decode_time (Gst.Video.CodecFrame frame); public int get_max_errors (); [Version (since = "1.4")] public bool get_needs_format (); [Version (since = "1.20")] public bool get_needs_sync_point (); public Gst.Video.CodecFrame get_oldest_frame (); public Gst.Video.CodecState get_output_state (); public bool get_packetized (); [Version (since = "1.4")] public size_t get_pending_frame_size (); [Version (since = "1.20")] public uint get_processed_subframe_index (Gst.Video.CodecFrame frame); [Version (since = "1.0.3")] public double get_qos_proportion (); [Version (since = "1.20")] public bool get_subframe_mode (); [NoWrapper] public virtual Gst.Caps getcaps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (owned Gst.Video.CodecFrame frame); [NoWrapper] [Version (since = "1.20")] public virtual bool handle_missing_data (Gst.ClockTime timestamp, Gst.ClockTime duration); public Gst.FlowReturn have_frame (); [Version (since = "1.20")] public Gst.FlowReturn have_last_subframe (Gst.Video.CodecFrame frame); public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); public virtual bool negotiate (); [NoWrapper] public virtual bool open (); [NoWrapper] public virtual Gst.FlowReturn parse (Gst.Video.CodecFrame frame, Gst.Base.Adapter adapter, bool at_eos); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); [Version (since = "1.6")] public Gst.Caps proxy_getcaps (Gst.Caps? caps, Gst.Caps? filter); [Version (since = "1.2.2")] public void release_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public void request_sync_point (Gst.Video.CodecFrame frame, Gst.Video.DecoderRequestSyncPointFlags flags); [NoWrapper] public virtual bool reset (bool hard); public void set_estimate_rate (bool enabled); [NoWrapper] public virtual bool set_format (Gst.Video.CodecState state); [Version (since = "1.16.")] public Gst.Video.CodecState set_interlaced_output_state (Gst.Video.Format fmt, Gst.Video.InterlaceMode interlace_mode, uint width, uint height, Gst.Video.CodecState? reference); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_max_errors (int num); [Version (since = "1.4")] public void set_needs_format (bool enabled); [Version (since = "1.20")] public void set_needs_sync_point (bool enabled); public Gst.Video.CodecState set_output_state (Gst.Video.Format fmt, uint width, uint height, Gst.Video.CodecState? reference); public void set_packetized (bool packetized); [Version (since = "1.20")] public void set_subframe_mode (bool subframe_mode); [Version (since = "1.6")] public void set_use_default_pad_acceptcaps (bool use); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool transform_meta (Gst.Video.CodecFrame frame, Gst.Meta meta); [NoAccessorMethod] [Version (since = "1.20")] public Gst.Video.DecoderRequestSyncPointFlags automatic_request_sync_point_flags { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public bool automatic_request_sync_points { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public bool discard_corrupted_frames { get; set; } [Version (since = "1.18")] public int max_errors { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public uint64 min_force_key_unit_interval { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public bool qos { get; set; } } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoDither")] public class Dither { public void free (); public void line (void* line, uint x, uint y, uint width); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_encoder_get_type ()")] [GIR (name = "VideoEncoder")] public abstract class Encoder : Gst.Element, Gst.Preset { [CCode (has_construct_function = false)] protected Encoder (); public Gst.Buffer allocate_output_buffer (size_t size); public Gst.FlowReturn allocate_output_frame (Gst.Video.CodecFrame frame, size_t size); [NoWrapper] public virtual bool close (); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual Gst.FlowReturn finish (); public Gst.FlowReturn finish_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.18")] public Gst.FlowReturn finish_subframe (Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool flush (); public void get_allocator (out Gst.Allocator allocator, out Gst.AllocationParams @params); public Gst.Video.CodecFrame get_frame (int frame_number); public GLib.List get_frames (); public void get_latency (out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); [Version (since = "1.14")] public Gst.ClockTimeDiff get_max_encode_time (Gst.Video.CodecFrame frame); [Version (since = "1.18")] public Gst.ClockTime get_min_force_key_unit_interval (); public Gst.Video.CodecFrame get_oldest_frame (); public Gst.Video.CodecState get_output_state (); [NoWrapper] public virtual Gst.Caps getcaps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (Gst.Video.CodecFrame frame); [Version (since = "1.14")] public bool is_qos_enabled (); public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); public virtual bool negotiate (); [NoWrapper] public virtual bool open (); [NoWrapper] public virtual Gst.FlowReturn pre_push (Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); public Gst.Caps proxy_getcaps (Gst.Caps? caps, Gst.Caps? filter); [NoWrapper] public virtual bool reset (bool hard); [NoWrapper] public virtual bool set_format (Gst.Video.CodecState state); public void set_headers (owned GLib.List headers); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); [Version (since = "1.18")] public void set_min_force_key_unit_interval (Gst.ClockTime interval); [Version (since = "1.6")] public void set_min_pts (Gst.ClockTime min_pts); public Gst.Video.CodecState set_output_state (owned Gst.Caps caps, Gst.Video.CodecState? reference); [Version (since = "1.14")] public void set_qos_enabled (bool enabled); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool transform_meta (Gst.Video.CodecFrame frame, Gst.Meta meta); [Version (since = "1.18")] public uint64 min_force_key_unit_interval { get; set; } [NoAccessorMethod] public bool qos { get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_filter_get_type ()")] [GIR (name = "VideoFilter")] public abstract class Filter : Gst.Base.Transform { public weak Gst.Video.Info in_info; public bool negotiated; public weak Gst.Video.Info out_info; [CCode (has_construct_function = false)] protected Filter (); [NoWrapper] public virtual bool set_info (Gst.Caps incaps, Gst.Video.Info in_info, Gst.Caps outcaps, Gst.Video.Info out_info); [NoWrapper] public virtual Gst.FlowReturn transform_frame (Gst.Video.Frame inframe, Gst.Video.Frame outframe); [NoWrapper] public virtual Gst.FlowReturn transform_frame_ip (Gst.Video.Frame frame); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_info_get_type ()")] [Compact] [GIR (name = "VideoInfo")] public class Info { [CCode (cname = "ABI.abi.field_order")] public Gst.Video.FieldOrder ABI_abi_field_order; [CCode (cname = "ABI.abi.multiview_flags")] public Gst.Video.MultiviewFlags ABI_abi_multiview_flags; [CCode (cname = "ABI.abi.multiview_mode")] public Gst.Video.MultiviewMode ABI_abi_multiview_mode; public Gst.Video.ChromaSite chroma_site; public Gst.Video.Colorimetry colorimetry; public weak Gst.Video.FormatInfo? finfo; public Gst.Video.Flags flags; public int fps_d; public int fps_n; public int height; public Gst.Video.InterlaceMode interlace_mode; [CCode (array_length = false)] public weak size_t offset[4]; public int par_d; public int par_n; public size_t size; [CCode (array_length = false)] public weak int stride[4]; public int views; public int width; [CCode (has_construct_function = false)] [Version (since = "1.6")] public Info (); public bool align (Gst.Video.Alignment align); [Version (since = "1.18")] public bool align_full (Gst.Video.Alignment align, out size_t plane_size); public bool convert (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); [Version (since = "1.6")] public Gst.Video.Info copy (); [Version (since = "1.6")] public void free (); public bool from_caps (Gst.Caps caps); public void init (); public bool is_equal (Gst.Video.Info other); public bool set_format (Gst.Video.Format format, uint width, uint height); [Version (since = "1.16")] public bool set_interlaced_format (Gst.Video.Format format, Gst.Video.InterlaceMode mode, uint width, uint height); public Gst.Caps to_caps (); [CCode (cname = "gst_video_info_new_from_caps", has_construct_function = false)] [Version (since = "1.20")] public Info.with_caps (Gst.Caps caps); } [CCode (cheader_filename = "gst/video/video.h", lower_case_cprefix = "gst_video_multiview_flagset_", type_id = "gst_video_multiview_flagset_get_type ()")] [GIR (name = "VideoMultiviewFlagsSet")] public class MultiviewFlagsSet : Gst.FlagSet { [CCode (has_construct_function = false)] protected MultiviewFlagsSet (); } [CCode (cheader_filename = "gst/video/video-overlay-composition.h", ref_function = "gst_video_overlay_composition_ref", type_id = "gst_video_overlay_composition_get_type ()", unref_function = "gst_video_overlay_composition_unref")] [Compact] [GIR (name = "VideoOverlayComposition")] public class OverlayComposition : Gst.MiniObject { [CCode (has_construct_function = false)] public OverlayComposition (Gst.Video.OverlayRectangle? rectangle); public void add_rectangle (Gst.Video.OverlayRectangle rectangle); public bool blend (Gst.Video.Frame video_buf); public Gst.Video.OverlayComposition copy (); public unowned Gst.Video.OverlayRectangle get_rectangle (uint n); public uint get_seqnum (); [DestroysInstance] [ReturnsModifiedPointer] public Gst.Video.OverlayComposition make_writable (); public uint n_rectangles (); } [CCode (cheader_filename = "gst/video/video-overlay-composition.h", ref_function = "gst_video_overlay_rectangle_ref", type_id = "gst_video_overlay_rectangle_get_type ()", unref_function = "gst_video_overlay_rectangle_unref")] [Compact] [GIR (name = "VideoOverlayRectangle")] public class OverlayRectangle : Gst.MiniObject { public Gst.Video.OverlayRectangle copy (); public Gst.Video.OverlayFormatFlags get_flags (); public float get_global_alpha (); public unowned Gst.Buffer get_pixels_argb (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_ayuv (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_raw (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_argb (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_ayuv (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_raw (Gst.Video.OverlayFormatFlags flags); public bool get_render_rectangle (out int render_x, out int render_y, out uint render_width, out uint render_height); public uint get_seqnum (); [CCode (has_construct_function = false)] public OverlayRectangle.raw (Gst.Buffer pixels, int render_x, int render_y, uint render_width, uint render_height, Gst.Video.OverlayFormatFlags flags); public void set_global_alpha (float global_alpha); public void set_render_rectangle (int render_x, int render_y, uint render_width, uint render_height); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoScaler")] public class Scaler { public void @2d (Gst.Video.Scaler vscale, Gst.Video.Format format, void* src, int src_stride, void* dest, int dest_stride, uint x, uint y, uint width, uint height); public void free (); public double get_coeff (uint out_offset, out uint in_offset, out uint n_taps); public uint get_max_taps (); public void horizontal (Gst.Video.Format format, void* src, void* dest, uint dest_offset, uint width); public void vertical (Gst.Video.Format format, void* src_lines, void* dest, uint dest_offset, uint width); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_sink_get_type ()")] [GIR (name = "VideoSink")] public class Sink : Gst.Base.Sink { public int height; public int width; [CCode (has_construct_function = false)] protected Sink (); [Version (deprecated = true, deprecated_since = "1.20")] public static void center_rect (Gst.Video.Rectangle src, Gst.Video.Rectangle dst, out Gst.Video.Rectangle result, bool scaling); [NoWrapper] [Version (since = "1.20")] public virtual bool set_info (Gst.Caps caps, Gst.Video.Info info); [NoWrapper] public virtual Gst.FlowReturn show_frame (Gst.Buffer buf); [NoAccessorMethod] public bool show_preroll_frame { get; set construct; } } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_time_code_get_type ()")] [Compact] [GIR (name = "VideoTimeCode")] [Version (since = "1.10")] public class TimeCode { public Gst.Video.TimeCodeConfig config; public uint field_count; public uint frames; public uint hours; public uint minutes; public uint seconds; [CCode (has_construct_function = false)] public TimeCode (uint fps_n, uint fps_d, GLib.DateTime latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); public void add_frames (int64 frames); [Version (since = "1.12")] public Gst.Video.TimeCode? add_interval (Gst.Video.TimeCodeInterval tc_inter); public void clear (); public int compare (Gst.Video.TimeCode tc2); public Gst.Video.TimeCode copy (); [CCode (has_construct_function = false)] public TimeCode.empty (); public uint64 frames_since_daily_jam (); public void free (); [CCode (has_construct_function = false)] [Version (since = "1.12")] public TimeCode.from_date_time (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [CCode (has_construct_function = false)] [Version (since = "1.16")] public TimeCode.from_date_time_full (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [CCode (has_construct_function = false)] [Version (since = "1.12")] public TimeCode.from_string (string tc_str); public void increment_frame (); public void init (uint fps_n, uint fps_d, GLib.DateTime? latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); [Version (since = "1.12")] public void init_from_date_time (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [Version (since = "1.16")] public bool init_from_date_time_full (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); public bool is_valid (); public uint64 nsec_since_daily_jam (); public GLib.DateTime? to_date_time (); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_time_code_interval_get_type ()")] [Compact] [GIR (name = "VideoTimeCodeInterval")] [Version (since = "1.12")] public class TimeCodeInterval { public uint frames; public uint hours; public uint minutes; public uint seconds; [CCode (has_construct_function = false)] public TimeCodeInterval (uint hours, uint minutes, uint seconds, uint frames); public void clear (); public Gst.Video.TimeCodeInterval copy (); public void free (); [CCode (has_construct_function = false)] public TimeCodeInterval.from_string (string tc_inter_str); public void init (uint hours, uint minutes, uint seconds, uint frames); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_vbi_encoder_get_type ()")] [Compact] [GIR (name = "VideoVBIEncoder")] [Version (since = "1.16")] public class VBIEncoder { [CCode (has_construct_function = false)] public VBIEncoder (Gst.Video.Format format, uint32 pixel_width); public bool add_ancillary (bool composite, uint8 DID, uint8 SDID_block_number, [CCode (array_length_cname = "data_count", array_length_pos = 4.1, array_length_type = "guint")] uint8[] data); public Gst.Video.VBIEncoder copy (); public void free (); public void write_line ([CCode (array_length = false, type = "guint8*")] uint8[] data); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_vbi_parser_get_type ()")] [Compact] [GIR (name = "VideoVBIParser")] [Version (since = "1.16")] public class VBIParser { [CCode (has_construct_function = false)] public VBIParser (Gst.Video.Format format, uint32 pixel_width); public void add_line ([CCode (array_length = false)] uint8[] data); public Gst.Video.VBIParser copy (); public void free (); public Gst.Video.VBIParserResult get_ancillary (out Gst.Video.Ancillary anc); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalance", lower_case_cprefix = "gst_color_balance_", type_cname = "GstColorBalanceInterface", type_id = "gst_color_balance_get_type ()")] [GIR (name = "ColorBalance")] public interface ColorBalance : GLib.Object { public abstract Gst.Video.ColorBalanceType get_balance_type (); public abstract int get_value (Gst.Video.ColorBalanceChannel channel); public abstract unowned GLib.List list_channels (); public abstract void set_value (Gst.Video.ColorBalanceChannel channel, int value); [HasEmitter] public virtual signal void value_changed (Gst.Video.ColorBalanceChannel channel, int value); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoDirectionInterface", type_id = "gst_video_direction_get_type ()")] [GIR (name = "VideoDirection")] [Version (since = "1.10")] public interface Direction : GLib.Object { [NoAccessorMethod] public abstract Gst.Video.OrientationMethod video_direction { get; set construct; } } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigation", lower_case_cprefix = "gst_navigation_", type_cname = "GstNavigationInterface", type_id = "gst_navigation_get_type ()")] [GIR (name = "Navigation")] public interface Navigation : GLib.Object { [Version (since = "1.22")] public static bool event_get_coordinates (Gst.Event event, out double x, out double y); public static Gst.Video.NavigationEventType event_get_type (Gst.Event event); [Version (since = "1.22")] public static Gst.Event event_new_command (Gst.Video.NavigationCommand command); [Version (since = "1.22")] public static Gst.Event event_new_key_press (string key, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_key_release (string key, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_button_press (int button, double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_button_release (int button, double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_move (double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_scroll (double x, double y, double delta_x, double delta_y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_cancel (Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_down (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_frame (Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_motion (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_up (uint identifier, double x, double y, Gst.Video.NavigationModifierType state); public static bool event_parse_command (Gst.Event event, out Gst.Video.NavigationCommand command); public static bool event_parse_key_event (Gst.Event event, out unowned string key); [Version (since = "1.22")] public static bool event_parse_modifier_state (Gst.Event event, Gst.Video.NavigationModifierType state); public static bool event_parse_mouse_button_event (Gst.Event event, out int button, out double x, out double y); public static bool event_parse_mouse_move_event (Gst.Event event, out double x, out double y); [Version (since = "1.18")] public static bool event_parse_mouse_scroll_event (Gst.Event event, out double x, out double y, out double delta_x, out double delta_y); [Version (since = "1.22")] public static bool event_parse_touch_event (Gst.Event event, out uint identifier, out double x, out double y, out double pressure); [Version (since = "1.22")] public static bool event_parse_touch_up_event (Gst.Event event, out uint identifier, out double x, out double y); [Version (since = "1.22")] public static bool event_set_coordinates (Gst.Event event, double x, double y); public static Gst.Video.NavigationMessageType message_get_type (Gst.Message message); public static Gst.Message message_new_angles_changed (Gst.Object src, uint cur_angle, uint n_angles); public static Gst.Message message_new_commands_changed (Gst.Object src); [Version (since = "1.6")] public static Gst.Message message_new_event (Gst.Object src, Gst.Event event); public static Gst.Message message_new_mouse_over (Gst.Object src, bool active); public static bool message_parse_angles_changed (Gst.Message message, out uint cur_angle, out uint n_angles); [Version (since = "1.6")] public static bool message_parse_event (Gst.Message message, out Gst.Event event); public static bool message_parse_mouse_over (Gst.Message message, out bool active); public static Gst.Video.NavigationQueryType query_get_type (Gst.Query query); public static Gst.Query query_new_angles (); public static Gst.Query query_new_commands (); public static bool query_parse_angles (Gst.Query query, out uint cur_angle, out uint n_angles); public static bool query_parse_commands_length (Gst.Query query, out uint n_cmds); public static bool query_parse_commands_nth (Gst.Query query, uint nth, out Gst.Video.NavigationCommand cmd); public static void query_set_angles (Gst.Query query, uint cur_angle, uint n_angles); public static void query_set_commandsv (Gst.Query query, [CCode (array_length_cname = "n_cmds", array_length_pos = 1.5)] Gst.Video.NavigationCommand[] cmds); public void send_command (Gst.Video.NavigationCommand command); [Version (deprecated = true, deprecated_since = "1.22")] public abstract void send_event (Gst.Structure structure); [Version (since = "1.22")] public abstract void send_event_simple (owned Gst.Event event); public void send_key_event (string event, string key); public void send_mouse_event (string event, int button, double x, double y); [Version (since = "1.18")] public void send_mouse_scroll_event (double x, double y, double delta_x, double delta_y); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoOrientationInterface", type_id = "gst_video_orientation_get_type ()")] [GIR (name = "VideoOrientation")] public interface Orientation : GLib.Object { [Version (since = "1.20")] public static bool from_tag (Gst.TagList taglist, out Gst.Video.OrientationMethod method); public abstract bool get_hcenter (out int center); public abstract bool get_hflip (out bool flip); public abstract bool get_vcenter (out int center); public abstract bool get_vflip (out bool flip); public abstract bool set_hcenter (int center); public abstract bool set_hflip (bool flip); public abstract bool set_vcenter (int center); public abstract bool set_vflip (bool flip); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoOverlayInterface", type_id = "gst_video_overlay_get_type ()")] [GIR (name = "VideoOverlay")] public interface Overlay : GLib.Object { public abstract void expose (); public void got_window_handle ([CCode (type = "guintptr")] uint* handle); public abstract void handle_events (bool handle_events); [Version (since = "1.14")] public static void install_properties (GLib.ObjectClass oclass, int last_prop_id); public void prepare_window_handle (); [Version (since = "1.14")] public static bool set_property (GLib.Object object, int last_prop_id, uint property_id, GLib.Value value); [NoWrapper] public virtual void set_render_rectangle (int x, int y, int width, int height); public abstract void set_window_handle ([CCode (type = "guintptr")] uint* handle); [CCode (cname = "gst_video_overlay_set_render_rectangle")] public bool try_set_render_rectangle (int x, int y, int width, int height); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAFDMeta")] [Version (since = "1.18")] public struct AFDMeta { public Gst.Meta meta; public uint8 field; public Gst.Video.AFDSpec spec; public Gst.Video.AFDValue afd; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAffineTransformationMeta")] [Version (since = "1.8")] public struct AffineTransformationMeta { public Gst.Meta meta; [CCode (array_length = false)] public weak float matrix[16]; public void apply_matrix ([CCode (array_length = false)] float matrix[16]); public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAlignment")] public struct Alignment { public uint padding_top; public uint padding_bottom; public uint padding_left; public uint padding_right; [CCode (array_length = false)] public weak uint stride_align[4]; public void reset (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAncillary")] [Version (since = "1.16")] public struct Ancillary { public uint8 DID; public uint8 SDID_block_number; public uint8 data_count; [CCode (array_length_cname = "data_count", array_length_type = "guint8")] public weak uint8[] data; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoBarMeta")] [Version (since = "1.18")] public struct BarMeta { public Gst.Meta meta; public uint8 field; public bool is_letterbox; public uint bar_data1; public uint bar_data2; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCaptionMeta")] [Version (since = "1.16")] public struct CaptionMeta { public Gst.Meta meta; public Gst.Video.CaptionType caption_type; [CCode (array_length_cname = "size", array_length_type = "gsize")] public weak uint8[] data; public size_t size; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCodecAlphaMeta")] [Version (since = "1.20")] public struct CodecAlphaMeta { public Gst.Meta meta; public weak Gst.Buffer buffer; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoColorPrimariesInfo")] [Version (since = "1.6")] public struct ColorPrimariesInfo { public Gst.Video.ColorPrimaries primaries; public double Wx; public double Wy; public double Rx; public double Ry; public double Gx; public double Gy; public double Bx; public double By; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoColorimetry")] public struct Colorimetry { public Gst.Video.ColorRange range; public Gst.Video.ColorMatrix matrix; public Gst.Video.TransferFunction transfer; public Gst.Video.ColorPrimaries primaries; public bool from_string (string color); [Version (since = "1.6")] public bool is_equal (Gst.Video.Colorimetry other); [Version (since = "1.22")] public bool is_equivalent (uint bitdepth, Gst.Video.Colorimetry other, uint other_bitdepth); public bool matches (string color); public string? to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoContentLightLevel")] [Version (since = "1.18")] public struct ContentLightLevel { public uint16 max_content_light_level; public uint16 max_frame_average_light_level; public bool add_to_caps (Gst.Caps caps); public bool from_caps (Gst.Caps caps); public bool from_string (string level); public void init (); [Version (since = "1.20")] public bool is_equal (Gst.Video.ContentLightLevel other); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCropMeta")] public struct CropMeta { public Gst.Meta meta; public uint x; public uint y; public uint width; public uint height; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoFormatInfo")] public struct FormatInfo { public Gst.Video.Format format; public weak string name; public weak string description; public Gst.Video.FormatFlags flags; public uint bits; public uint n_components; [CCode (array_length = false)] public weak uint shift[4]; [CCode (array_length = false)] public weak uint depth[4]; [CCode (array_length = false)] public weak int pixel_stride[4]; public uint n_planes; [CCode (array_length = false)] public weak uint plane[4]; [CCode (array_length = false)] public weak uint poffset[4]; [CCode (array_length = false)] public weak uint w_sub[4]; [CCode (array_length = false)] public weak uint h_sub[4]; public Gst.Video.Format unpack_format; public weak Gst.Video.FormatUnpack unpack_func; public int pack_lines; public weak Gst.Video.FormatPack pack_func; public Gst.Video.TileMode tile_mode; public uint tile_ws; public uint tile_hs; [Version (since = "1.18")] public void component (uint plane, out int components); [Version (since = "1.22")] public int extrapolate_stride (int plane, int stride); [Version (since = "1.22")] public uint get_tile_sizes (uint plane, uint? out_ws, uint? out_hs); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoFrame")] public struct Frame { public weak Gst.Video.Info info; public Gst.Video.FrameFlags flags; public weak Gst.Buffer buffer; public void* meta; public int id; [CCode (array_length = false)] public void* data[4]; [CCode (array_length = false, cname = "map")] public Gst.MapInfo map_info[4]; public bool copy (Gst.Video.Frame src); public bool copy_plane (Gst.Video.Frame src, uint plane); public bool map (Gst.Video.Info info, Gst.Buffer buffer, Gst.MapFlags flags); public bool map_id (Gst.Video.Info info, Gst.Buffer buffer, int id, Gst.MapFlags flags); public void unmap (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoGLTextureUploadMeta")] public struct GLTextureUploadMeta { public Gst.Meta meta; public Gst.Video.GLTextureOrientation texture_orientation; public uint n_textures; [CCode (array_length = false)] public weak Gst.Video.GLTextureType texture_type[4]; public static unowned Gst.MetaInfo? get_info (); public bool upload (uint texture_id); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMasteringDisplayInfo")] [Version (since = "1.18")] public struct MasteringDisplayInfo { [CCode (array_length = false)] public weak Gst.Video.MasteringDisplayInfoCoordinates display_primaries[3]; public Gst.Video.MasteringDisplayInfoCoordinates white_point; public uint32 max_display_mastering_luminance; public uint32 min_display_mastering_luminance; public bool add_to_caps (Gst.Caps caps); public bool from_caps (Gst.Caps caps); public bool from_string (string mastering); public void init (); public bool is_equal (Gst.Video.MasteringDisplayInfo other); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMasteringDisplayInfoCoordinates")] [Version (since = "1.18")] public struct MasteringDisplayInfoCoordinates { public uint16 x; public uint16 y; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMeta")] public struct Meta { public Gst.Meta meta; public weak Gst.Buffer buffer; public Gst.Video.FrameFlags flags; public Gst.Video.Format format; public int id; public uint width; public uint height; public uint n_planes; [CCode (array_length = false)] public weak size_t offset[4]; [CCode (array_length = false)] public weak int stride[4]; [CCode (cname = "map")] public weak Gst.Video.MetaMapVFunc map_v; [CCode (cname = "unmap")] public weak Gst.Video.MetaUnmapVFunc unmap_v; public Gst.Video.Alignment alignment; public static unowned Gst.MetaInfo? get_info (); [Version (since = "1.18")] public bool get_plane_height ([CCode (array_length = false)] out unowned uint plane_height[4]); [Version (since = "1.18")] public bool get_plane_size ([CCode (array_length = false)] out unowned size_t plane_size[4]); public bool map (uint plane, Gst.MapInfo info, out void* data, out int stride, Gst.MapFlags flags); [Version (since = "1.18")] public bool set_alignment (Gst.Video.Alignment alignment); public bool unmap (uint plane, Gst.MapInfo info); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMetaTransform")] public struct MetaTransform { public weak Gst.Video.Info in_info; public weak Gst.Video.Info out_info; public static GLib.Quark scale_get_quark (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoOverlayCompositionMeta")] public struct OverlayCompositionMeta { public Gst.Meta meta; public weak Gst.Video.OverlayComposition overlay; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoRectangle")] public struct Rectangle { public int x; public int y; public int w; public int h; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoRegionOfInterestMeta")] public struct RegionOfInterestMeta { public Gst.Meta meta; public GLib.Quark roi_type; public int id; public int parent_id; public uint x; public uint y; public uint w; public uint h; public weak GLib.List @params; [Version (since = "1.14")] public void add_param (owned Gst.Structure s); public static unowned Gst.MetaInfo? get_info (); [Version (since = "1.14")] public unowned Gst.Structure? get_param (string name); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoResampler")] [Version (since = "1.6")] public struct Resampler { public int in_size; public int out_size; public uint max_taps; public uint n_phases; public uint32 offset; public uint32 phase; public uint32 n_taps; public double taps; public void clear (); public bool init (Gst.Video.ResamplerMethod method, Gst.Video.ResamplerFlags flags, uint n_phases, uint n_taps, double shift, uint in_size, uint out_size, Gst.Structure options); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoSEIUserDataUnregisteredMeta")] [Version (since = "1.22")] public struct SEIUserDataUnregisteredMeta { public Gst.Meta meta; [CCode (array_length = false)] public weak uint8 uuid[16]; public uint8 data; public size_t size; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoTimeCodeConfig")] [Version (since = "1.10")] public struct TimeCodeConfig { public uint fps_n; public uint fps_d; public Gst.Video.TimeCodeFlags flags; public weak GLib.DateTime latest_daily_jam; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoTimeCodeMeta")] [Version (since = "1.10")] public struct TimeCodeMeta { public Gst.Meta meta; public weak Gst.Video.TimeCode tc; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_AFD_SPEC_", type_id = "gst_video_afd_spec_get_type ()")] [GIR (name = "VideoAFDSpec")] [Version (since = "1.18")] public enum AFDSpec { DVB_ETSI, ATSC_A53, SMPTE_ST2016_1 } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_AFD_", type_id = "gst_video_afd_value_get_type ()")] [GIR (name = "VideoAFDValue")] [Version (since = "1.18")] public enum AFDValue { UNAVAILABLE, @16_9_TOP_ALIGNED, @14_9_TOP_ALIGNED, GREATER_THAN_16_9, @4_3_FULL_16_9_FULL, @4_3_FULL_4_3_PILLAR, @16_9_LETTER_16_9_FULL, @14_9_LETTER_14_9_PILLAR, @4_3_FULL_14_9_CENTER, @16_9_LETTER_14_9_CENTER, @16_9_LETTER_4_3_CENTER } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ALPHA_MODE_", type_id = "gst_video_alpha_mode_get_type ()")] [GIR (name = "VideoAlphaMode")] [Version (since = "1.6")] public enum AlphaMode { COPY, SET, MULT } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ANCILLARY_DID_", type_id = "gst_video_ancillary_did_get_type ()")] [GIR (name = "VideoAncillaryDID")] [Version (since = "1.16")] public enum AncillaryDID { UNDEFINED, DELETION, HANC_3G_AUDIO_DATA_FIRST, HANC_3G_AUDIO_DATA_LAST, HANC_HDTV_AUDIO_DATA_FIRST, HANC_HDTV_AUDIO_DATA_LAST, HANC_SDTV_AUDIO_DATA_1_FIRST, HANC_SDTV_AUDIO_DATA_1_LAST, CAMERA_POSITION, HANC_ERROR_DETECTION, HANC_SDTV_AUDIO_DATA_2_FIRST, HANC_SDTV_AUDIO_DATA_2_LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ANCILLARY_DID16_", type_id = "gst_video_ancillary_di_d16_get_type ()")] [GIR (name = "VideoAncillaryDID16")] [Version (since = "1.16")] public enum AncillaryDID16 { S334_EIA_708, S334_EIA_608, S2016_3_AFD_BAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_BUFFER_FLAG_", type_id = "gst_video_buffer_flags_get_type ()")] [Flags] [GIR (name = "VideoBufferFlags")] public enum BufferFlags { INTERLACED, TFF, RFF, ONEFIELD, MULTIPLE_VIEW, FIRST_IN_BUNDLE, TOP_FIELD, BOTTOM_FIELD, MARKER, LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CAPTION_TYPE_", type_id = "gst_video_caption_type_get_type ()")] [GIR (name = "VideoCaptionType")] [Version (since = "1.16")] public enum CaptionType { UNKNOWN, CEA608_RAW, CEA608_S334_1A, CEA708_RAW, CEA708_CDP; public static Gst.Video.CaptionType from_caps (Gst.Caps caps); public Gst.Caps to_caps (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_FLAG_", type_id = "gst_video_chroma_flags_get_type ()")] [Flags] [GIR (name = "VideoChromaFlags")] public enum ChromaFlags { NONE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_METHOD_", type_id = "gst_video_chroma_method_get_type ()")] [GIR (name = "VideoChromaMethod")] public enum ChromaMethod { NEAREST, LINEAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_MODE_", type_id = "gst_video_chroma_mode_get_type ()")] [GIR (name = "VideoChromaMode")] [Version (since = "1.6")] public enum ChromaMode { FULL, UPSAMPLE_ONLY, DOWNSAMPLE_ONLY, NONE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_SITE_", type_id = "gst_video_chroma_site_get_type ()")] [Flags] [GIR (name = "VideoChromaSite")] public enum ChromaSite { UNKNOWN, NONE, H_COSITED, V_COSITED, ALT_LINE, COSITED, JPEG, MPEG2, DV; [Version (since = "1.20")] public static Gst.Video.ChromaSite from_string (string s); [Version (since = "1.20")] public string? to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CODEC_FRAME_FLAG_", type_id = "gst_video_codec_frame_flags_get_type ()")] [Flags] [GIR (name = "VideoCodecFrameFlags")] public enum CodecFrameFlags { DECODE_ONLY, SYNC_POINT, FORCE_KEYFRAME, FORCE_KEYFRAME_HEADERS, [Version (since = "1.20")] CORRUPTED } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalanceType", cprefix = "GST_COLOR_BALANCE_", type_id = "gst_color_balance_type_get_type ()")] [GIR (name = "ColorBalanceType")] public enum ColorBalanceType { HARDWARE, SOFTWARE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_MATRIX_", type_id = "gst_video_color_matrix_get_type ()")] [GIR (name = "VideoColorMatrix")] public enum ColorMatrix { UNKNOWN, RGB, FCC, BT709, BT601, SMPTE240M, BT2020; [Version (since = "1.18")] public static Gst.Video.ColorMatrix from_iso (uint value); [Version (since = "1.6")] public bool get_Kr_Kb (out double Kr, out double Kb); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_PRIMARIES_", type_id = "gst_video_color_primaries_get_type ()")] [GIR (name = "VideoColorPrimaries")] public enum ColorPrimaries { UNKNOWN, BT709, BT470M, BT470BG, SMPTE170M, SMPTE240M, FILM, BT2020, ADOBERGB, SMPTEST428, SMPTERP431, SMPTEEG432, EBU3213; [Version (since = "1.18")] public static Gst.Video.ColorPrimaries from_iso (uint value); [Version (since = "1.6")] public unowned Gst.Video.ColorPrimariesInfo? get_info (); [Version (since = "1.22")] public bool is_equivalent (Gst.Video.ColorPrimaries other); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_RANGE_", type_id = "gst_video_color_range_get_type ()")] [GIR (name = "VideoColorRange")] public enum ColorRange { UNKNOWN, @0_255, @16_235; public void offsets (Gst.Video.FormatInfo info, [CCode (array_length = false)] out unowned int offset[4], [CCode (array_length = false)] out unowned int scale[4]); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DECODER_REQUEST_SYNC_POINT_", type_id = "gst_video_decoder_request_sync_point_flags_get_type ()")] [Flags] [GIR (name = "VideoDecoderRequestSyncPointFlags")] [Version (since = "1.20")] public enum DecoderRequestSyncPointFlags { DISCARD_INPUT, CORRUPT_OUTPUT } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DITHER_FLAG_", type_id = "gst_video_dither_flags_get_type ()")] [Flags] [GIR (name = "VideoDitherFlags")] public enum DitherFlags { NONE, INTERLACED, QUANTIZE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DITHER_", type_id = "gst_video_dither_method_get_type ()")] [GIR (name = "VideoDitherMethod")] public enum DitherMethod { NONE, VERTERR, FLOYD_STEINBERG, SIERRA_LITE, BAYER } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FIELD_ORDER_", type_id = "gst_video_field_order_get_type ()")] [GIR (name = "VideoFieldOrder")] [Version (since = "1.12")] public enum FieldOrder { UNKNOWN, TOP_FIELD_FIRST, BOTTOM_FIELD_FIRST; public static Gst.Video.FieldOrder from_string (string order); public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FLAG_", type_id = "gst_video_flags_get_type ()")] [Flags] [GIR (name = "VideoFlags")] public enum Flags { NONE, VARIABLE_FPS, PREMULTIPLIED_ALPHA } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FORMAT_", type_id = "gst_video_format_get_type ()")] [GIR (name = "VideoFormat")] public enum Format { UNKNOWN, ENCODED, I420, YV12, YUY2, UYVY, AYUV, [CCode (cname = "GST_VIDEO_FORMAT_RGBx")] RGBX, [CCode (cname = "GST_VIDEO_FORMAT_BGRx")] BGRX, [CCode (cname = "GST_VIDEO_FORMAT_xRGB")] XRGB, [CCode (cname = "GST_VIDEO_FORMAT_xBGR")] XBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, Y41B, Y42B, YVYU, Y444, [CCode (cname = "GST_VIDEO_FORMAT_v210")] V210, [CCode (cname = "GST_VIDEO_FORMAT_v216")] V216, NV12, NV21, GRAY8, GRAY16_BE, GRAY16_LE, [CCode (cname = "GST_VIDEO_FORMAT_v308")] V308, RGB16, BGR16, RGB15, BGR15, UYVP, A420, RGB8P, YUV9, YVU9, IYU1, ARGB64, AYUV64, [CCode (cname = "GST_VIDEO_FORMAT_r210")] R210, I420_10BE, I420_10LE, I422_10BE, I422_10LE, Y444_10BE, Y444_10LE, GBR, GBR_10BE, GBR_10LE, NV16, NV24, NV12_64Z32, A420_10BE, A420_10LE, A422_10BE, A422_10LE, A444_10BE, A444_10LE, NV61, P010_10BE, P010_10LE, IYU2, VYUY, GBRA, GBRA_10BE, GBRA_10LE, GBR_12BE, GBR_12LE, GBRA_12BE, GBRA_12LE, I420_12BE, I420_12LE, I422_12BE, I422_12LE, Y444_12BE, Y444_12LE, GRAY10_LE32, NV12_10LE32, NV16_10LE32, NV12_10LE40, Y210, Y410, VUYA, BGR10A2_LE, RGB10A2_LE, Y444_16BE, Y444_16LE, P016_BE, P016_LE, P012_BE, P012_LE, Y212_BE, Y212_LE, Y412_BE, Y412_LE, [Version (since = "1.18")] NV12_4L4, [Version (since = "1.18")] NV12_32L32, [Version (since = "1.20")] RGBP, [Version (since = "1.20")] BGRP, [Version (since = "1.20")] AV12, [Version (since = "1.20")] ARGB64_LE, [Version (since = "1.20")] ARGB64_BE, [Version (since = "1.20")] RGBA64_LE, [Version (since = "1.20")] RGBA64_BE, [Version (since = "1.20")] BGRA64_LE, [Version (since = "1.20")] BGRA64_BE, [Version (since = "1.20")] ABGR64_LE, [Version (since = "1.20")] ABGR64_BE, [Version (since = "1.22")] NV12_16L32S, [Version (since = "1.22")] NV12_8L128, [Version (since = "1.22")] NV12_10BE_8L128; public static Gst.Video.Format from_fourcc (uint32 fourcc); public static Gst.Video.Format from_masks (int depth, int bpp, int endianness, uint red_mask, uint green_mask, uint blue_mask, uint alpha_mask); public static Gst.Video.Format from_string (string format); public unowned Gst.Video.FormatInfo? get_info (); [Version (since = "1.2")] public void* get_palette (out size_t size); public uint32 to_fourcc (); public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FORMAT_FLAG_", type_id = "gst_video_format_flags_get_type ()")] [Flags] [GIR (name = "VideoFormatFlags")] public enum FormatFlags { YUV, RGB, GRAY, ALPHA, LE, PALETTE, COMPLEX, UNPACK, TILED, [Version (since = "1.22")] SUBTILES } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FRAME_FLAG_", type_id = "gst_video_frame_flags_get_type ()")] [Flags] [GIR (name = "VideoFrameFlags")] public enum FrameFlags { NONE, INTERLACED, TFF, RFF, ONEFIELD, MULTIPLE_VIEW, FIRST_IN_BUNDLE, TOP_FIELD, BOTTOM_FIELD } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FRAME_MAP_FLAG_", type_id = "gst_video_frame_map_flags_get_type ()")] [Flags] [GIR (name = "VideoFrameMapFlags")] [Version (since = "1.6")] public enum FrameMapFlags { NO_REF, LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GL_TEXTURE_ORIENTATION_X_", type_id = "gst_video_gl_texture_orientation_get_type ()")] [GIR (name = "VideoGLTextureOrientation")] public enum GLTextureOrientation { NORMAL_Y_NORMAL, NORMAL_Y_FLIP, FLIP_Y_NORMAL, FLIP_Y_FLIP } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GL_TEXTURE_TYPE_", type_id = "gst_video_gl_texture_type_get_type ()")] [GIR (name = "VideoGLTextureType")] public enum GLTextureType { LUMINANCE, LUMINANCE_ALPHA, RGB16, RGB, RGBA, R, RG } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GAMMA_MODE_", type_id = "gst_video_gamma_mode_get_type ()")] [GIR (name = "VideoGammaMode")] [Version (since = "1.6")] public enum GammaMode { NONE, REMAP } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_INTERLACE_MODE_", type_id = "gst_video_interlace_mode_get_type ()")] [GIR (name = "VideoInterlaceMode")] public enum InterlaceMode { PROGRESSIVE, INTERLEAVED, MIXED, FIELDS, ALTERNATE; [Version (since = "1.6")] public static Gst.Video.InterlaceMode from_string (string mode); [Version (since = "1.6")] public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MATRIX_MODE_", type_id = "gst_video_matrix_mode_get_type ()")] [GIR (name = "VideoMatrixMode")] [Version (since = "1.6")] public enum MatrixMode { FULL, INPUT_ONLY, OUTPUT_ONLY, NONE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_FLAGS_", type_id = "gst_video_multiview_flags_get_type ()")] [Flags] [GIR (name = "VideoMultiviewFlags")] public enum MultiviewFlags { NONE, RIGHT_VIEW_FIRST, LEFT_FLIPPED, LEFT_FLOPPED, RIGHT_FLIPPED, RIGHT_FLOPPED, HALF_ASPECT, MIXED_MONO } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_FRAME_PACKING_", type_id = "gst_video_multiview_frame_packing_get_type ()")] [GIR (name = "VideoMultiviewFramePacking")] public enum MultiviewFramePacking { NONE, MONO, LEFT, RIGHT, SIDE_BY_SIDE, SIDE_BY_SIDE_QUINCUNX, COLUMN_INTERLEAVED, ROW_INTERLEAVED, TOP_BOTTOM, CHECKERBOARD } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_MODE_", type_id = "gst_video_multiview_mode_get_type ()")] [GIR (name = "VideoMultiviewMode")] public enum MultiviewMode { NONE, MONO, LEFT, RIGHT, SIDE_BY_SIDE, SIDE_BY_SIDE_QUINCUNX, COLUMN_INTERLEAVED, ROW_INTERLEAVED, TOP_BOTTOM, CHECKERBOARD, FRAME_BY_FRAME, MULTIVIEW_FRAME_BY_FRAME, SEPARATED; [Version (since = "1.6")] public static Gst.Video.MultiviewMode from_caps_string (string caps_mview_mode); [Version (since = "1.6")] public unowned string to_caps_string (); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationCommand", cprefix = "GST_NAVIGATION_COMMAND_", type_id = "gst_navigation_command_get_type ()")] [GIR (name = "NavigationCommand")] public enum NavigationCommand { INVALID, MENU1, MENU2, MENU3, MENU4, MENU5, MENU6, MENU7, LEFT, RIGHT, UP, DOWN, ACTIVATE, PREV_ANGLE, NEXT_ANGLE } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationEventType", cprefix = "GST_NAVIGATION_EVENT_", type_id = "gst_navigation_event_type_get_type ()")] [GIR (name = "NavigationEventType")] public enum NavigationEventType { INVALID, KEY_PRESS, KEY_RELEASE, MOUSE_BUTTON_PRESS, MOUSE_BUTTON_RELEASE, MOUSE_MOVE, COMMAND, [Version (since = "1.18")] MOUSE_SCROLL, [Version (since = "1.22")] TOUCH_DOWN, [Version (since = "1.22")] TOUCH_MOTION, [Version (since = "1.22")] TOUCH_UP, [Version (since = "1.22")] TOUCH_FRAME, [Version (since = "1.22")] TOUCH_CANCEL } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationMessageType", cprefix = "GST_NAVIGATION_MESSAGE_", type_id = "gst_navigation_message_type_get_type ()")] [GIR (name = "NavigationMessageType")] public enum NavigationMessageType { INVALID, MOUSE_OVER, COMMANDS_CHANGED, ANGLES_CHANGED, EVENT } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationModifierType", cprefix = "GST_NAVIGATION_MODIFIER_", type_id = "gst_navigation_modifier_type_get_type ()")] [Flags] [GIR (name = "NavigationModifierType")] [Version (since = "1.22")] public enum NavigationModifierType { NONE, SHIFT_MASK, LOCK_MASK, CONTROL_MASK, ALT_MASK, BUTTON1_MASK, BUTTON2_MASK, BUTTON3_MASK, BUTTON4_MASK, BUTTON5_MASK, SUPER_MASK, HYPER_MASK, META_MASK, MASK } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationQueryType", cprefix = "GST_NAVIGATION_QUERY_", type_id = "gst_navigation_query_type_get_type ()")] [GIR (name = "NavigationQueryType")] public enum NavigationQueryType { INVALID, COMMANDS, ANGLES } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ORIENTATION_", type_id = "gst_video_orientation_method_get_type ()")] [GIR (name = "VideoOrientationMethod")] [Version (since = "1.10")] public enum OrientationMethod { IDENTITY, @90R, @180, @90L, HORIZ, VERT, UL_LR, UR_LL, AUTO, CUSTOM } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_OVERLAY_FORMAT_FLAG_", type_id = "gst_video_overlay_format_flags_get_type ()")] [Flags] [GIR (name = "VideoOverlayFormatFlags")] public enum OverlayFormatFlags { NONE, PREMULTIPLIED_ALPHA, GLOBAL_ALPHA } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_PACK_FLAG_", type_id = "gst_video_pack_flags_get_type ()")] [Flags] [GIR (name = "VideoPackFlags")] public enum PackFlags { NONE, TRUNCATE_RANGE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_PRIMARIES_MODE_", type_id = "gst_video_primaries_mode_get_type ()")] [GIR (name = "VideoPrimariesMode")] [Version (since = "1.6")] public enum PrimariesMode { NONE, MERGE_ONLY, FAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_RESAMPLER_FLAG_", type_id = "gst_video_resampler_flags_get_type ()")] [Flags] [GIR (name = "VideoResamplerFlags")] [Version (since = "1.6")] public enum ResamplerFlags { NONE, HALF_TAPS } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_RESAMPLER_METHOD_", type_id = "gst_video_resampler_method_get_type ()")] [GIR (name = "VideoResamplerMethod")] [Version (since = "1.6")] public enum ResamplerMethod { NEAREST, LINEAR, CUBIC, SINC, LANCZOS } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_SCALER_FLAG_", type_id = "gst_video_scaler_flags_get_type ()")] [Flags] [GIR (name = "VideoScalerFlags")] public enum ScalerFlags { NONE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TILE_MODE_", type_id = "gst_video_tile_mode_get_type ()")] [GIR (name = "VideoTileMode")] public enum TileMode { UNKNOWN, ZFLIPZ_2X2, [Version (since = "1.18")] LINEAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TILE_TYPE_", type_id = "gst_video_tile_type_get_type ()")] [GIR (name = "VideoTileType")] public enum TileType { INDEXED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TIME_CODE_FLAGS_", type_id = "gst_video_time_code_flags_get_type ()")] [Flags] [GIR (name = "VideoTimeCodeFlags")] [Version (since = "1.10")] public enum TimeCodeFlags { NONE, DROP_FRAME, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TRANSFER_", type_id = "gst_video_transfer_function_get_type ()")] [GIR (name = "VideoTransferFunction")] public enum TransferFunction { UNKNOWN, GAMMA10, GAMMA18, GAMMA20, GAMMA22, BT709, SMPTE240M, SRGB, GAMMA28, LOG100, LOG316, BT2020_12, ADOBERGB, BT2020_10, SMPTE2084, ARIB_STD_B67, [Version (since = "1.18")] BT601; [Version (since = "1.20")] public double decode (double val); [Version (since = "1.20")] public double encode (double val); [Version (since = "1.18")] public static Gst.Video.TransferFunction from_iso (uint value); [Version (since = "1.18")] public bool is_equivalent (uint from_bpp, Gst.Video.TransferFunction to_func, uint to_bpp); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_VBI_PARSER_RESULT_", type_id = "gst_video_vbi_parser_result_get_type ()")] [GIR (name = "VideoVBIParserResult")] [Version (since = "1.16")] public enum VBIParserResult { DONE, OK, ERROR } [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate bool AffineTransformationGetMatrix (Gst.Video.AffineTransformationMeta meta, float matrix); [CCode (cheader_filename = "gst/video/video.h", instance_pos = 2.9)] public delegate void ConvertSampleCallback (Gst.Sample sample, GLib.Error error); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate void FormatPack (Gst.Video.FormatInfo info, Gst.Video.PackFlags flags, void* src, int sstride, void* data, int stride, Gst.Video.ChromaSite chroma_site, int y, int width); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate void FormatUnpack (Gst.Video.FormatInfo info, Gst.Video.PackFlags flags, void* dest, void* data, int stride, int x, int y, int width); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate bool GLTextureUpload (Gst.Video.GLTextureUploadMeta meta, uint texture_id); [CCode (cheader_filename = "gst/video/video.h", has_target = false, has_typedef = false)] public delegate bool MetaMapVFunc (Gst.Video.Meta meta, uint plane, Gst.MapInfo info, void* data, int stride, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h", has_target = false, has_typedef = false)] public delegate bool MetaUnmapVFunc (Gst.Video.Meta meta, uint plane, Gst.MapInfo info); [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_AFFINE_TRANSFORMATION_META")] public const string BUFFER_POOL_OPTION_VIDEO_AFFINE_TRANSFORMATION_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT")] public const string BUFFER_POOL_OPTION_VIDEO_ALIGNMENT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META")] [Version (since = "1.2.2")] public const string BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_META")] public const string BUFFER_POOL_OPTION_VIDEO_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_FORMAT_INTERLACED")] [Version (since = "1.16.")] public const string CAPS_FEATURE_FORMAT_INTERLACED; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META")] public const string CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META")] public const string CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_META")] public const string CAPS_FEATURE_META_GST_VIDEO_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION")] public const string CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2020")] public const string COLORIMETRY_BT2020; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2020_10")] public const string COLORIMETRY_BT2020_10; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2100_HLG")] public const string COLORIMETRY_BT2100_HLG; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2100_PQ")] public const string COLORIMETRY_BT2100_PQ; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT601")] public const string COLORIMETRY_BT601; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT709")] public const string COLORIMETRY_BT709; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_SMPTE240M")] public const string COLORIMETRY_SMPTE240M; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_SRGB")] public const string COLORIMETRY_SRGB; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_A")] public const int COMP_A; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_B")] public const int COMP_B; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_G")] public const int COMP_G; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_INDEX")] public const int COMP_INDEX; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_PALETTE")] public const int COMP_PALETTE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_R")] public const int COMP_R; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_U")] public const int COMP_U; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_V")] public const int COMP_V; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_Y")] public const int COMP_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ALPHA_MODE")] public const string CONVERTER_OPT_ALPHA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ALPHA_VALUE")] public const string CONVERTER_OPT_ALPHA_VALUE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ASYNC_TASKS")] [Version (since = "1.20")] public const string CONVERTER_OPT_ASYNC_TASKS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_BORDER_ARGB")] public const string CONVERTER_OPT_BORDER_ARGB; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_CHROMA_MODE")] public const string CONVERTER_OPT_CHROMA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_CHROMA_RESAMPLER_METHOD")] public const string CONVERTER_OPT_CHROMA_RESAMPLER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT")] public const string CONVERTER_OPT_DEST_HEIGHT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_WIDTH")] public const string CONVERTER_OPT_DEST_WIDTH; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_X")] public const string CONVERTER_OPT_DEST_X; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_Y")] public const string CONVERTER_OPT_DEST_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DITHER_METHOD")] public const string CONVERTER_OPT_DITHER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION")] public const string CONVERTER_OPT_DITHER_QUANTIZATION; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_FILL_BORDER")] public const string CONVERTER_OPT_FILL_BORDER; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_GAMMA_MODE")] public const string CONVERTER_OPT_GAMMA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_MATRIX_MODE")] public const string CONVERTER_OPT_MATRIX_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_PRIMARIES_MODE")] public const string CONVERTER_OPT_PRIMARIES_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD")] public const string CONVERTER_OPT_RESAMPLER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS")] public const string CONVERTER_OPT_RESAMPLER_TAPS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT")] public const string CONVERTER_OPT_SRC_HEIGHT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_WIDTH")] public const string CONVERTER_OPT_SRC_WIDTH; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_X")] public const string CONVERTER_OPT_SRC_X; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_Y")] public const string CONVERTER_OPT_SRC_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_THREADS")] public const string CONVERTER_OPT_THREADS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_MAX_ERRORS")] public const int DECODER_MAX_ERRORS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_SINK_NAME")] public const string DECODER_SINK_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_SRC_NAME")] public const string DECODER_SRC_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_ENCODER_SINK_NAME")] public const string ENCODER_SINK_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_ENCODER_SRC_NAME")] public const string ENCODER_SRC_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_FORMATS_ALL")] public const string FORMATS_ALL; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_FPS_RANGE")] public const string FPS_RANGE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_MAX_COMPONENTS")] public const int MAX_COMPONENTS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_MAX_PLANES")] public const int MAX_PLANES; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_COLORSPACE_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_COLORSPACE_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_ORIENTATION_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_ORIENTATION_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_SIZE_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_SIZE_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_CUBIC_B")] public const string RESAMPLER_OPT_CUBIC_B; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_CUBIC_C")] public const string RESAMPLER_OPT_CUBIC_C; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_ENVELOPE")] public const string RESAMPLER_OPT_ENVELOPE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_MAX_TAPS")] public const string RESAMPLER_OPT_MAX_TAPS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_SHARPEN")] public const string RESAMPLER_OPT_SHARPEN; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_SHARPNESS")] public const string RESAMPLER_OPT_SHARPNESS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_SCALER_OPT_DITHER_METHOD")] public const string SCALER_OPT_DITHER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_SIZE_RANGE")] public const string SIZE_RANGE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_TYPE_MASK")] public const int TILE_TYPE_MASK; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_TYPE_SHIFT")] public const int TILE_TYPE_SHIFT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_X_TILES_MASK")] public const int TILE_X_TILES_MASK; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_Y_TILES_SHIFT")] public const int TILE_Y_TILES_SHIFT; [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type afd_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoAFDMeta.get_info")] public static unowned Gst.MetaInfo? afd_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type affine_transformation_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoAffineTransformationMeta.get_info")] public static unowned Gst.MetaInfo? affine_transformation_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type bar_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoBarMeta.get_info")] public static unowned Gst.MetaInfo? bar_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static bool blend (Gst.Video.Frame dest, Gst.Video.Frame src, int x, int y, float global_alpha); [CCode (cheader_filename = "gst/video/video.h")] public static void blend_scale_linear_RGBA (Gst.Video.Info src, Gst.Buffer src_buffer, int dest_height, int dest_width, out unowned Gst.Video.Info dest, out Gst.Buffer dest_buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_afd_meta")] [Version (since = "1.18")] public static unowned Gst.Video.AFDMeta? buffer_add_video_afd_meta (Gst.Buffer buffer, uint8 field, Gst.Video.AFDSpec spec, Gst.Video.AFDValue afd); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_affine_transformation_meta")] [Version (since = "1.8")] public static unowned Gst.Video.AffineTransformationMeta? buffer_add_video_affine_transformation_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_bar_meta")] [Version (since = "1.18")] public static unowned Gst.Video.BarMeta? buffer_add_video_bar_meta (Gst.Buffer buffer, uint8 field, bool is_letterbox, uint bar_data1, uint bar_data2); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_caption_meta")] [Version (since = "1.16")] public static unowned Gst.Video.CaptionMeta? buffer_add_video_caption_meta (Gst.Buffer buffer, Gst.Video.CaptionType caption_type, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "gsize")] uint8[] data); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_codec_alpha_meta")] [Version (since = "1.20")] public static unowned Gst.Video.CodecAlphaMeta? buffer_add_video_codec_alpha_meta (Gst.Buffer buffer, owned Gst.Buffer alpha_buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_gl_texture_upload_meta")] public static unowned Gst.Video.GLTextureUploadMeta? buffer_add_video_gl_texture_upload_meta (Gst.Buffer buffer, Gst.Video.GLTextureOrientation texture_orientation, uint n_textures, Gst.Video.GLTextureType texture_type, [CCode (delegate_target_pos = 5.5)] Gst.Video.GLTextureUpload upload, GLib.BoxedCopyFunc user_data_copy, GLib.BoxedFreeFunc user_data_free); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_meta")] public static unowned Gst.Video.Meta? buffer_add_video_meta (Gst.Buffer buffer, Gst.Video.FrameFlags flags, Gst.Video.Format format, uint width, uint height); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_meta_full")] public static unowned Gst.Video.Meta? buffer_add_video_meta_full (Gst.Buffer buffer, Gst.Video.FrameFlags flags, Gst.Video.Format format, uint width, uint height, uint n_planes, [CCode (array_length = false)] size_t offset[4], [CCode (array_length = false)] int stride[4]); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_overlay_composition_meta")] public static unowned Gst.Video.OverlayCompositionMeta? buffer_add_video_overlay_composition_meta (Gst.Buffer buf, Gst.Video.OverlayComposition? comp); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_region_of_interest_meta")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_add_video_region_of_interest_meta (Gst.Buffer buffer, string roi_type, uint x, uint y, uint w, uint h); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_region_of_interest_meta_id")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_add_video_region_of_interest_meta_id (Gst.Buffer buffer, GLib.Quark roi_type, uint x, uint y, uint w, uint h); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_sei_user_data_unregistered_meta")] [Version (since = "1.22")] public static unowned Gst.Video.SEIUserDataUnregisteredMeta? buffer_add_video_sei_user_data_unregistered_meta (Gst.Buffer buffer, uint8 uuid, uint8 data, size_t size); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_time_code_meta")] [Version (since = "1.10")] public static unowned Gst.Video.TimeCodeMeta? buffer_add_video_time_code_meta (Gst.Buffer buffer, Gst.Video.TimeCode tc); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_time_code_meta_full")] [Version (since = "1.10")] public static unowned Gst.Video.TimeCodeMeta? buffer_add_video_time_code_meta_full (Gst.Buffer buffer, uint fps_n, uint fps_d, GLib.DateTime latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_meta")] public static unowned Gst.Video.Meta? buffer_get_video_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_meta_id")] public static unowned Gst.Video.Meta? buffer_get_video_meta_id (Gst.Buffer buffer, int id); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_region_of_interest_meta_id")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_get_video_region_of_interest_meta_id (Gst.Buffer buffer, int id); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_pool_config_get_video_alignment")] public static bool buffer_pool_config_get_video_alignment (Gst.Structure config, Gst.Video.Alignment align); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_pool_config_set_video_alignment")] public static void buffer_pool_config_set_video_alignment (Gst.Structure config, Gst.Video.Alignment align); [CCode (cheader_filename = "gst/video/video.h")] public static bool calculate_display_ratio (out uint dar_n, out uint dar_d, uint video_width, uint video_height, uint video_par_n, uint video_par_d, uint display_par_n, uint display_par_d); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type caption_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionMeta.get_info")] public static unowned Gst.MetaInfo? caption_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionType.from_caps", since = "1.16")] public static Gst.Video.CaptionType caption_type_from_caps (Gst.Caps caps); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionType.to_caps", since = "1.16")] public static Gst.Caps caption_type_to_caps (Gst.Video.CaptionType type); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.20")] public static void center_rect (Gst.Video.Rectangle src, Gst.Video.Rectangle dst, out Gst.Video.Rectangle result, bool scaling); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20")] public static Gst.Video.ChromaSite chroma_from_string (string s); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoChromaSite.from_string", since = "1.20")] public static Gst.Video.ChromaSite chroma_site_from_string (string s); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoChromaSite.to_string", since = "1.20")] public static string? chroma_site_to_string (Gst.Video.ChromaSite site); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20")] public static unowned string chroma_to_string (Gst.Video.ChromaSite site); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.20")] public static GLib.Type codec_alpha_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCodecAlphaMeta.get_info", since = "1.20")] public static unowned Gst.MetaInfo? codec_alpha_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.from_iso", since = "1.18")] public static Gst.Video.ColorMatrix color_matrix_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.get_Kr_Kb", since = "1.6")] public static bool color_matrix_get_Kr_Kb (Gst.Video.ColorMatrix matrix, out double Kr, out double Kb); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.to_iso", since = "1.18")] public static uint color_matrix_to_iso (Gst.Video.ColorMatrix matrix); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.from_iso", since = "1.18")] public static Gst.Video.ColorPrimaries color_primaries_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.get_info", since = "1.6")] public static unowned Gst.Video.ColorPrimariesInfo? color_primaries_get_info (Gst.Video.ColorPrimaries primaries); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.is_equivalent", since = "1.22")] public static bool color_primaries_is_equivalent (Gst.Video.ColorPrimaries primaries, Gst.Video.ColorPrimaries other); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.to_iso", since = "1.18")] public static uint color_primaries_to_iso (Gst.Video.ColorPrimaries primaries); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorRange.offsets")] public static void color_range_offsets (Gst.Video.ColorRange range, Gst.Video.FormatInfo info, [CCode (array_length = false)] out unowned int offset[4], [CCode (array_length = false)] out unowned int scale[4]); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20", since = "1.6")] public static double color_transfer_decode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20", since = "1.6")] public static double color_transfer_encode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Sample convert_sample (Gst.Sample sample, Gst.Caps to_caps, Gst.ClockTime timeout) throws GLib.Error; [CCode (cheader_filename = "gst/video/video.h")] public static void convert_sample_async (Gst.Sample sample, Gst.Caps to_caps, Gst.ClockTime timeout, owned Gst.Video.ConvertSampleCallback callback); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type crop_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCropMeta.get_info")] public static unowned Gst.MetaInfo? crop_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_is_force_key_unit (Gst.Event event); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_downstream_force_key_unit (Gst.ClockTime timestamp, Gst.ClockTime stream_time, Gst.ClockTime running_time, bool all_headers, uint count); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_still_frame (bool in_still); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_upstream_force_key_unit (Gst.ClockTime running_time, bool all_headers, uint count); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_downstream_force_key_unit (Gst.Event event, out Gst.ClockTime timestamp, out Gst.ClockTime stream_time, out Gst.ClockTime running_time, out bool all_headers, out uint count); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_still_frame (Gst.Event event, out bool in_still); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_upstream_force_key_unit (Gst.Event event, out Gst.ClockTime running_time, out bool all_headers, out uint count); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFieldOrder.from_string", since = "1.12")] public static Gst.Video.FieldOrder field_order_from_string (string order); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFieldOrder.to_string", since = "1.12")] public static unowned string field_order_to_string (Gst.Video.FieldOrder order); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_fourcc")] public static Gst.Video.Format format_from_fourcc (uint32 fourcc); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_masks")] public static Gst.Video.Format format_from_masks (int depth, int bpp, int endianness, uint red_mask, uint green_mask, uint blue_mask, uint alpha_mask); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_string")] public static Gst.Video.Format format_from_string (string format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.get_info")] public static unowned Gst.Video.FormatInfo? format_get_info (Gst.Video.Format format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.get_palette", since = "1.2")] public static void* format_get_palette (Gst.Video.Format format, out size_t size); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.to_fourcc")] public static uint32 format_to_fourcc (Gst.Video.Format format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.to_string")] public static unowned string format_to_string (Gst.Video.Format format); [CCode (array_length_pos = 0.1, array_length_type = "guint", cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static unowned Gst.Video.Format[] formats_raw (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFrame.map")] public static bool frame_map (out Gst.Video.Frame frame, Gst.Video.Info info, Gst.Buffer buffer, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFrame.map_id")] public static bool frame_map_id (out Gst.Video.Frame frame, Gst.Video.Info info, Gst.Buffer buffer, int id, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type gl_texture_upload_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoGLTextureUploadMeta.get_info")] public static unowned Gst.MetaInfo? gl_texture_upload_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static bool guess_framerate (Gst.ClockTime duration, out int dest_n, out int dest_d); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInfo.from_caps")] public static bool info_from_caps (out unowned Gst.Video.Info info, Gst.Caps caps); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInfo.init")] public static void info_init (out unowned Gst.Video.Info info); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInterlaceMode.from_string", since = "1.6")] public static Gst.Video.InterlaceMode interlace_mode_from_string (string mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInterlaceMode.to_string", since = "1.6")] public static unowned string interlace_mode_to_string (Gst.Video.InterlaceMode mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static bool is_common_aspect_ratio (int width, int height, int par_n, int par_d); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_is_video_overlay_prepare_window_handle_message")] public static bool is_video_overlay_prepare_window_handle_message (Gst.Message msg); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static Gst.Caps make_raw_caps ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] Gst.Video.Format[]? formats); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static Gst.Caps make_raw_caps_with_features ([CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint")] Gst.Video.Format[]? formats, owned Gst.CapsFeatures? features); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMasteringDisplayInfo.from_string", since = "1.18")] public static bool mastering_display_info_from_string (out Gst.Video.MasteringDisplayInfo minfo, string mastering); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMeta.get_info")] public static unowned Gst.MetaInfo? meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMetaTransform.scale_get_quark")] public static GLib.Quark meta_transform_scale_get_quark (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_height_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_size_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_width_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_mono_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_unpacked_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static bool multiview_guess_half_aspect (Gst.Video.MultiviewMode mv_mode, uint width, uint height, uint par_n, uint par_d); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMultiviewMode.from_caps_string", since = "1.6")] public static Gst.Video.MultiviewMode multiview_mode_from_caps_string (string caps_mview_mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMultiviewMode.to_caps_string", since = "1.6")] public static unowned string multiview_mode_to_caps_string (Gst.Video.MultiviewMode mview_mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static void multiview_video_info_change_mode (Gst.Video.Info info, Gst.Video.MultiviewMode out_mview_mode, Gst.Video.MultiviewFlags out_mview_flags); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_get_coordinates")] [Version (replacement = "Navigation.event_get_coordinates", since = "1.22")] public static bool navigation_event_get_coordinates (Gst.Event event, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_get_type")] [Version (replacement = "Navigation.event_get_type")] public static Gst.Video.NavigationEventType navigation_event_get_type (Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_command")] [Version (replacement = "Navigation.event_new_command", since = "1.22")] public static Gst.Event navigation_event_new_command (Gst.Video.NavigationCommand command); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_key_press")] [Version (replacement = "Navigation.event_new_key_press", since = "1.22")] public static Gst.Event navigation_event_new_key_press (string key, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_key_release")] [Version (replacement = "Navigation.event_new_key_release", since = "1.22")] public static Gst.Event navigation_event_new_key_release (string key, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_button_press")] [Version (replacement = "Navigation.event_new_mouse_button_press", since = "1.22")] public static Gst.Event navigation_event_new_mouse_button_press (int button, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_button_release")] [Version (replacement = "Navigation.event_new_mouse_button_release", since = "1.22")] public static Gst.Event navigation_event_new_mouse_button_release (int button, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_move")] [Version (replacement = "Navigation.event_new_mouse_move", since = "1.22")] public static Gst.Event navigation_event_new_mouse_move (double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_scroll")] [Version (replacement = "Navigation.event_new_mouse_scroll", since = "1.22")] public static Gst.Event navigation_event_new_mouse_scroll (double x, double y, double delta_x, double delta_y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_cancel")] [Version (replacement = "Navigation.event_new_touch_cancel", since = "1.22")] public static Gst.Event navigation_event_new_touch_cancel (Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_down")] [Version (replacement = "Navigation.event_new_touch_down", since = "1.22")] public static Gst.Event navigation_event_new_touch_down (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_frame")] [Version (replacement = "Navigation.event_new_touch_frame", since = "1.22")] public static Gst.Event navigation_event_new_touch_frame (Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_motion")] [Version (replacement = "Navigation.event_new_touch_motion", since = "1.22")] public static Gst.Event navigation_event_new_touch_motion (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_up")] [Version (replacement = "Navigation.event_new_touch_up", since = "1.22")] public static Gst.Event navigation_event_new_touch_up (uint identifier, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_command")] [Version (replacement = "Navigation.event_parse_command")] public static bool navigation_event_parse_command (Gst.Event event, out Gst.Video.NavigationCommand command); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_key_event")] [Version (replacement = "Navigation.event_parse_key_event")] public static bool navigation_event_parse_key_event (Gst.Event event, out unowned string key); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_modifier_state")] [Version (replacement = "Navigation.event_parse_modifier_state", since = "1.22")] public static bool navigation_event_parse_modifier_state (Gst.Event event, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_button_event")] [Version (replacement = "Navigation.event_parse_mouse_button_event")] public static bool navigation_event_parse_mouse_button_event (Gst.Event event, out int button, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_move_event")] [Version (replacement = "Navigation.event_parse_mouse_move_event")] public static bool navigation_event_parse_mouse_move_event (Gst.Event event, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_scroll_event")] [Version (replacement = "Navigation.event_parse_mouse_scroll_event", since = "1.18")] public static bool navigation_event_parse_mouse_scroll_event (Gst.Event event, out double x, out double y, out double delta_x, out double delta_y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_touch_event")] [Version (replacement = "Navigation.event_parse_touch_event", since = "1.22")] public static bool navigation_event_parse_touch_event (Gst.Event event, out uint identifier, out double x, out double y, out double pressure); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_touch_up_event")] [Version (replacement = "Navigation.event_parse_touch_up_event", since = "1.22")] public static bool navigation_event_parse_touch_up_event (Gst.Event event, out uint identifier, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_set_coordinates")] [Version (replacement = "Navigation.event_set_coordinates", since = "1.22")] public static bool navigation_event_set_coordinates (Gst.Event event, double x, double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_get_type")] [Version (replacement = "Navigation.message_get_type")] public static Gst.Video.NavigationMessageType navigation_message_get_type (Gst.Message message); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_angles_changed")] [Version (replacement = "Navigation.message_new_angles_changed")] public static Gst.Message navigation_message_new_angles_changed (Gst.Object src, uint cur_angle, uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_commands_changed")] [Version (replacement = "Navigation.message_new_commands_changed")] public static Gst.Message navigation_message_new_commands_changed (Gst.Object src); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_event")] [Version (replacement = "Navigation.message_new_event", since = "1.6")] public static Gst.Message navigation_message_new_event (Gst.Object src, Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_mouse_over")] [Version (replacement = "Navigation.message_new_mouse_over")] public static Gst.Message navigation_message_new_mouse_over (Gst.Object src, bool active); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_angles_changed")] [Version (replacement = "Navigation.message_parse_angles_changed")] public static bool navigation_message_parse_angles_changed (Gst.Message message, out uint cur_angle, out uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_event")] [Version (replacement = "Navigation.message_parse_event", since = "1.6")] public static bool navigation_message_parse_event (Gst.Message message, out Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_mouse_over")] [Version (replacement = "Navigation.message_parse_mouse_over")] public static bool navigation_message_parse_mouse_over (Gst.Message message, out bool active); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_get_type")] [Version (replacement = "Navigation.query_get_type")] public static Gst.Video.NavigationQueryType navigation_query_get_type (Gst.Query query); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_new_angles")] [Version (replacement = "Navigation.query_new_angles")] public static Gst.Query navigation_query_new_angles (); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_new_commands")] [Version (replacement = "Navigation.query_new_commands")] public static Gst.Query navigation_query_new_commands (); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_angles")] [Version (replacement = "Navigation.query_parse_angles")] public static bool navigation_query_parse_angles (Gst.Query query, out uint cur_angle, out uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_commands_length")] [Version (replacement = "Navigation.query_parse_commands_length")] public static bool navigation_query_parse_commands_length (Gst.Query query, out uint n_cmds); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_commands_nth")] [Version (replacement = "Navigation.query_parse_commands_nth")] public static bool navigation_query_parse_commands_nth (Gst.Query query, uint nth, out Gst.Video.NavigationCommand cmd); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_set_angles")] [Version (replacement = "Navigation.query_set_angles")] public static void navigation_query_set_angles (Gst.Query query, uint cur_angle, uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_set_commandsv")] [Version (replacement = "Navigation.query_set_commandsv")] public static void navigation_query_set_commandsv (Gst.Query query, [CCode (array_length_cname = "n_cmds", array_length_pos = 1.5)] Gst.Video.NavigationCommand[] cmds); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOrientation.from_tag", since = "1.20")] public static bool orientation_from_tag (Gst.TagList taglist, out Gst.Video.OrientationMethod method); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type overlay_composition_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlayCompositionMeta.get_info")] public static unowned Gst.MetaInfo? overlay_composition_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlay.install_properties", since = "1.14")] public static void overlay_install_properties (GLib.ObjectClass oclass, int last_prop_id); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlay.set_property", since = "1.14")] public static bool overlay_set_property (GLib.Object object, int last_prop_id, uint property_id, GLib.Value value); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type region_of_interest_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoRegionOfInterestMeta.get_info")] public static unowned Gst.MetaInfo? region_of_interest_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static GLib.Type sei_user_data_unregistered_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoSEIUserDataUnregisteredMeta.get_info", since = "1.22")] public static unowned Gst.MetaInfo? sei_user_data_unregistered_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static bool sei_user_data_unregistered_parse_precision_time_stamp (Gst.Video.SEIUserDataUnregisteredMeta user_data, out uint8 status, out uint64 precision_time_stamp); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.4")] public static uint tile_get_index (Gst.Video.TileMode mode, int x, int y, int x_tiles, int y_tiles); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type time_code_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTimeCodeMeta.get_info")] public static unowned Gst.MetaInfo? time_code_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.decode", since = "1.20")] public static double transfer_function_decode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.encode", since = "1.20")] public static double transfer_function_encode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.from_iso", since = "1.18")] public static Gst.Video.TransferFunction transfer_function_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.is_equivalent", since = "1.18")] public static bool transfer_function_is_equivalent (Gst.Video.TransferFunction from_func, uint from_bpp, Gst.Video.TransferFunction to_func, uint to_bpp); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.to_iso", since = "1.18")] public static uint transfer_function_to_iso (Gst.Video.TransferFunction func); } } dino-0.4.3/plugins/signal-protocol/0000755000000000000000000000000014452563620015751 5ustar rootrootdino-0.4.3/plugins/signal-protocol/CMakeLists.txt0000644000000000000000000001124614452563620020515 0ustar rootrootfind_package(GCrypt REQUIRED) find_packages(SIGNAL_PROTOCOL_PACKAGES REQUIRED Gee GLib GObject ) vala_precompile(SIGNAL_PROTOCOL_VALA_C SOURCES "src/context.vala" "src/simple_iks.vala" "src/simple_ss.vala" "src/simple_pks.vala" "src/simple_spks.vala" "src/store.vala" "src/util.vala" CUSTOM_VAPIS ${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-public.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-native.vapi PACKAGES ${SIGNAL_PROTOCOL_PACKAGES} GENERATE_VAPI signal-protocol-vala GENERATE_HEADER signal-protocol-vala ) set(C_HEADERS_SRC "") set(C_HEADERS_TARGET "") if(NOT BUILD_LIBSIGNAL_IN_TREE) # libsignal-protocol-c has a history of breaking compatibility on the patch level # we'll have to check compatibility for every new release # distro maintainers may update this dependency after compatibility tests find_package(SignalProtocol 2.3.2 REQUIRED) else() add_subdirectory(libsignal-protocol-c EXCLUDE_FROM_ALL) set_property(TARGET curve25519 PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET protobuf-c PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET signal-protocol-c PROPERTY POSITION_INDEPENDENT_CODE ON) set(SIGNAL_PROTOCOL_C_HEADERS signal_protocol.h signal_protocol_types.h curve.h hkdf.h ratchet.h protocol.h session_state.h session_record.h session_pre_key.h session_builder.h session_cipher.h key_helper.h sender_key.h sender_key_state.h sender_key_record.h group_session_builder.h group_cipher.h fingerprint.h device_consistency.h ) file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/exports/signal") foreach(f ${SIGNAL_PROTOCOL_C_HEADERS}) list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}") list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal/${f}") add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/signal/${f}" COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" "${CMAKE_BINARY_DIR}/exports/signal/${f}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" COMMENT Copy header file signal/${f} ) endforeach(f) endif() list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h") list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal_helper.h") add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/signal_helper.h" COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h" "${CMAKE_BINARY_DIR}/exports/signal_helper.h" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h" COMMENT Copy header file signal_helper.h ) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/exports/signal-protocol.vapi COMMAND cat "${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-public.vapi" "${CMAKE_BINARY_DIR}/exports/signal-protocol-vala.vapi" > "${CMAKE_BINARY_DIR}/exports/signal-protocol.vapi" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-public.vapi ${CMAKE_BINARY_DIR}/exports/signal-protocol-vala.vapi ) add_custom_target(signal-protocol-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/signal-protocol.vapi ${CMAKE_BINARY_DIR}/exports/signal-protocol-vala.h ${C_HEADERS_TARGET} ) set(CFLAGS ${VALA_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src -I${CMAKE_CURRENT_SOURCE_DIR}/src) add_definitions(${CFLAGS}) add_library(signal-protocol-vala STATIC ${SIGNAL_PROTOCOL_VALA_C} ${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.c) add_dependencies(signal-protocol-vala signal-protocol-vapi) target_link_libraries(signal-protocol-vala ${SIGNAL_PROTOCOL_PACKAGES} gcrypt signal-protocol-c m) set_property(TARGET signal-protocol-vala PROPERTY POSITION_INDEPENDENT_CODE ON) if(BUILD_TESTS) vala_precompile(SIGNAL_TEST_VALA_C SOURCES "tests/common.vala" "tests/testcase.vala" "tests/curve25519.vala" "tests/hkdf.vala" "tests/session_builder.vala" CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/signal-protocol-vala_internal.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-public.vapi ${CMAKE_CURRENT_SOURCE_DIR}/vapi/signal-protocol-native.vapi PACKAGES ${SIGNAL_PROTOCOL_PACKAGES} ) set(CFLAGS ${VALA_CFLAGS} -I${CMAKE_CURRENT_BINARY_DIR}/signal-protocol) add_executable(signal-protocol-vala-test ${SIGNAL_TEST_VALA_C}) add_dependencies(signal-protocol-vala-test signal-protocol-vala) target_link_libraries(signal-protocol-vala-test signal-protocol-vala ${SIGNAL_PROTOCOL_PACKAGES}) endif(BUILD_TESTS) dino-0.4.3/plugins/signal-protocol/libsignal-protocol-c/0000755000000000000000000000000014452563620021774 5ustar rootrootdino-0.4.3/plugins/signal-protocol/src/0000755000000000000000000000000014452563620016540 5ustar rootrootdino-0.4.3/plugins/signal-protocol/src/context.vala0000644000000000000000000000761514452563620021102 0ustar rootrootnamespace Signal { public class Context { internal NativeContext native_context; private RecMutex mutex = RecMutex(); static void locking_function_lock(void* user_data) { Context ctx = (Context) user_data; ctx.mutex.lock(); } static void locking_function_unlock(void* user_data) { Context ctx = (Context) user_data; ctx.mutex.unlock(); } static void stderr_log(LogLevel level, string message, size_t len, void* user_data) { printerr(@"$level: $message\n"); } public Context(bool log = false) throws Error { throw_by_code(NativeContext.create(out native_context, this), "Error initializing native context"); throw_by_code(native_context.set_locking_functions(locking_function_lock, locking_function_unlock), "Error initializing native locking functions"); if (log) native_context.set_log_function(stderr_log); setup_crypto_provider(native_context); } public Store create_store() { return new Store(this); } public void randomize(uint8[] data) throws Error { throw_by_code(Signal.native_random(data)); } public SignedPreKeyRecord generate_signed_pre_key(IdentityKeyPair identity_key_pair, int32 id, uint64 timestamp = 0) throws Error { if (timestamp == 0) timestamp = new DateTime.now_utc().to_unix(); SignedPreKeyRecord res; throw_by_code(Protocol.KeyHelper.generate_signed_pre_key(out res, identity_key_pair, id, timestamp, native_context)); return res; } public Gee.Set generate_pre_keys(uint start, uint count) throws Error { Gee.Set res = new Gee.HashSet(); for(uint i = start; i < start+count; i++) { ECKeyPair pair = generate_key_pair(); PreKeyRecord record; throw_by_code(PreKeyRecord.create(out record, i, pair)); res.add(record); } return res; } public ECPublicKey decode_public_key(uint8[] bytes) throws Error { ECPublicKey public_key; throw_by_code(curve_decode_point(out public_key, bytes, native_context), "Error decoding public key"); return public_key; } public ECPrivateKey decode_private_key(uint8[] bytes) throws Error { ECPrivateKey private_key; throw_by_code(curve_decode_private_point(out private_key, bytes, native_context), "Error decoding private key"); return private_key; } public ECKeyPair generate_key_pair() throws Error { ECKeyPair key_pair; throw_by_code(curve_generate_key_pair(native_context, out key_pair), "Error generating key pair"); return key_pair; } public uint8[] calculate_signature(ECPrivateKey signing_key, uint8[] message) throws Error { Buffer signature; throw_by_code(Curve.calculate_signature(native_context, out signature, signing_key, message), "Error calculating signature"); return signature.data; } public SignalMessage deserialize_signal_message(uint8[] data) throws Error { SignalMessage res; throw_by_code(signal_message_deserialize(out res, data, native_context)); return res; } public SignalMessage copy_signal_message(CiphertextMessage original) throws Error { SignalMessage res; throw_by_code(signal_message_copy(out res, (SignalMessage) original, native_context)); return res; } public PreKeySignalMessage deserialize_pre_key_signal_message(uint8[] data) throws Error { PreKeySignalMessage res; throw_by_code(pre_key_signal_message_deserialize(out res, data, native_context)); return res; } public PreKeySignalMessage copy_pre_key_signal_message(CiphertextMessage original) throws Error { PreKeySignalMessage res; throw_by_code(pre_key_signal_message_copy(out res, (PreKeySignalMessage) original, native_context)); return res; } } } dino-0.4.3/plugins/signal-protocol/src/signal_helper.c0000644000000000000000000002564114452563620021530 0ustar rootroot#include #include signal_type_base* signal_type_ref_vapi(void* instance) { g_return_val_if_fail(instance != NULL, NULL); signal_type_ref(instance); return instance; } signal_type_base* signal_type_unref_vapi(void* instance) { g_return_val_if_fail(instance != NULL, NULL); signal_type_unref(instance); return NULL; } signal_protocol_address* signal_protocol_address_new(const gchar* name, int32_t device_id) { g_return_val_if_fail(name != NULL, NULL); signal_protocol_address* address = malloc(sizeof(signal_protocol_address)); address->device_id = -1; address->name = NULL; signal_protocol_address_set_name(address, name); signal_protocol_address_set_device_id(address, device_id); return address; } void signal_protocol_address_free(signal_protocol_address* ptr) { g_return_if_fail(ptr != NULL); if (ptr->name) { g_free((void*)ptr->name); } return free(ptr); } void signal_protocol_address_set_name(signal_protocol_address* self, const gchar* name) { g_return_if_fail(self != NULL); g_return_if_fail(name != NULL); gchar* n = g_malloc(strlen(name)+1); memcpy(n, name, strlen(name)); n[strlen(name)] = 0; if (self->name) { g_free((void*)self->name); } self->name = n; self->name_len = strlen(n); } gchar* signal_protocol_address_get_name(signal_protocol_address* self) { g_return_val_if_fail(self != NULL, NULL); g_return_val_if_fail(self->name != NULL, 0); gchar* res = g_malloc(sizeof(char) * (self->name_len + 1)); memcpy(res, self->name, self->name_len); res[self->name_len] = 0; return res; } int32_t signal_protocol_address_get_device_id(signal_protocol_address* self) { g_return_val_if_fail(self != NULL, -1); return self->device_id; } void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_t device_id) { g_return_if_fail(self != NULL); self->device_id = device_id; } int signal_vala_randomize(uint8_t *data, size_t len) { gcry_randomize(data, len, GCRY_STRONG_RANDOM); return SG_SUCCESS; } int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data) { gcry_randomize(data, len, GCRY_STRONG_RANDOM); return SG_SUCCESS; } int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) { gcry_mac_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); if (!ctx) return SG_ERR_NOMEM; if (gcry_mac_open(ctx, GCRY_MAC_HMAC_SHA256, 0, 0)) { free(ctx); return SG_ERR_UNKNOWN; } if (gcry_mac_setkey(*ctx, key, key_len)) { free(ctx); return SG_ERR_UNKNOWN; } *hmac_context = ctx; return SG_SUCCESS; } int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) { gcry_mac_hd_t* ctx = hmac_context; if (gcry_mac_write(*ctx, data, data_len)) return SG_ERR_UNKNOWN; return SG_SUCCESS; } int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data) { size_t len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256); uint8_t md[len]; gcry_mac_hd_t* ctx = hmac_context; if (gcry_mac_read(*ctx, md, &len)) return SG_ERR_UNKNOWN; signal_buffer *output_buffer = signal_buffer_create(md, len); if (!output_buffer) return SG_ERR_NOMEM; *output = output_buffer; return SG_SUCCESS; } void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data) { gcry_mac_hd_t* ctx = hmac_context; if (ctx) { gcry_mac_close(*ctx); free(ctx); } } int signal_vala_sha512_digest_init(void **digest_context, void *user_data) { gcry_md_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); if (!ctx) return SG_ERR_NOMEM; if (gcry_md_open(ctx, GCRY_MD_SHA512, 0)) { free(ctx); return SG_ERR_UNKNOWN; } *digest_context = ctx; return SG_SUCCESS; } int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) { gcry_md_hd_t* ctx = digest_context; gcry_md_write(*ctx, data, data_len); return SG_SUCCESS; } int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data) { size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512); gcry_md_hd_t* ctx = digest_context; uint8_t* md = gcry_md_read(*ctx, GCRY_MD_SHA512); if (!md) return SG_ERR_UNKNOWN; gcry_md_reset(*ctx); signal_buffer *output_buffer = signal_buffer_create(md, len); free(md); if (!output_buffer) return SG_ERR_NOMEM; *output = output_buffer; return SG_SUCCESS; } void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data) { gcry_md_hd_t* ctx = digest_context; if (ctx) { gcry_md_close(*ctx); free(ctx); } } const int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) { switch (key_len) { case 16: *algo = GCRY_CIPHER_AES128; break; case 24: *algo = GCRY_CIPHER_AES192; break; case 32: *algo = GCRY_CIPHER_AES256; break; default: return SG_ERR_UNKNOWN; } switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: *mode = GCRY_CIPHER_MODE_CBC; break; case SG_CIPHER_AES_CTR_NOPADDING: *mode = GCRY_CIPHER_MODE_CTR; break; case SG_CIPHER_AES_GCM_NOPADDING: *mode = GCRY_CIPHER_MODE_GCM; break; default: return SG_ERR_UNKNOWN; } return SG_SUCCESS; } int signal_vala_encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data) { int algo, mode, error_code = SG_ERR_UNKNOWN; if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; gcry_cipher_hd_t ctx = {0}; if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; signal_buffer* padded = 0; signal_buffer* out_buf = 0; goto no_error; error: gcry_cipher_close(ctx); if (padded != 0) { signal_buffer_bzero_free(padded); } if (out_buf != 0) { signal_buffer_free(out_buf); } return error_code; no_error: if (gcry_cipher_setkey(ctx, key, key_len)) goto error; uint8_t tag_len = 0, pad_len = 0; switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; pad_len = 16 - (plaintext_len % 16); if (pad_len == 0) pad_len = 16; break; case SG_CIPHER_AES_CTR_NOPADDING: if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; break; case SG_CIPHER_AES_GCM_NOPADDING: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; tag_len = 16; break; default: return SG_ERR_UNKNOWN; } size_t padded_len = plaintext_len + pad_len; padded = signal_buffer_alloc(padded_len); if (padded == 0) { error_code = SG_ERR_NOMEM; goto error; } memset(signal_buffer_data(padded) + plaintext_len, pad_len, pad_len); memcpy(signal_buffer_data(padded), plaintext, plaintext_len); out_buf = signal_buffer_alloc(padded_len + tag_len); if (out_buf == 0) { error_code = SG_ERR_NOMEM; goto error; } if (gcry_cipher_encrypt(ctx, signal_buffer_data(out_buf), padded_len, signal_buffer_data(padded), padded_len)) goto error; if (tag_len > 0) { if (gcry_cipher_gettag(ctx, signal_buffer_data(out_buf) + padded_len, tag_len)) goto error; } *output = out_buf; out_buf = 0; signal_buffer_bzero_free(padded); padded = 0; gcry_cipher_close(ctx); return SG_SUCCESS; } int signal_vala_decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data) { int algo, mode, error_code = SG_ERR_UNKNOWN; *output = 0; if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; if (ciphertext_len == 0) return SG_ERR_INVAL; gcry_cipher_hd_t ctx = {0}; if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; signal_buffer* out_buf = 0; goto no_error; error: gcry_cipher_close(ctx); if (out_buf != 0) { signal_buffer_bzero_free(out_buf); } return error_code; no_error: if (gcry_cipher_setkey(ctx, key, key_len)) goto error; uint8_t tag_len = 0, pkcs_pad = FALSE; switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; pkcs_pad = TRUE; break; case SG_CIPHER_AES_CTR_NOPADDING: if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; break; case SG_CIPHER_AES_GCM_NOPADDING: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; if (ciphertext_len < 16) goto error; tag_len = 16; break; default: goto error; } size_t padded_len = ciphertext_len - tag_len; out_buf = signal_buffer_alloc(padded_len); if (out_buf == 0) { error_code = SG_ERR_NOMEM; goto error; } if (gcry_cipher_decrypt(ctx, signal_buffer_data(out_buf), signal_buffer_len(out_buf), ciphertext, padded_len)) goto error; if (tag_len > 0) { if (gcry_cipher_checktag(ctx, ciphertext + padded_len, tag_len)) goto error; } if (pkcs_pad) { uint8_t pad_len = signal_buffer_data(out_buf)[padded_len - 1]; if (pad_len > 16 || pad_len > padded_len) goto error; *output = signal_buffer_create(signal_buffer_data(out_buf), padded_len - pad_len); signal_buffer_bzero_free(out_buf); out_buf = 0; } else { *output = out_buf; out_buf = 0; } gcry_cipher_close(ctx); return SG_SUCCESS; } void setup_signal_vala_crypto_provider(signal_context *context) { gcry_check_version(NULL); signal_crypto_provider provider = { .random_func = signal_vala_random_generator, .hmac_sha256_init_func = signal_vala_hmac_sha256_init, .hmac_sha256_update_func = signal_vala_hmac_sha256_update, .hmac_sha256_final_func = signal_vala_hmac_sha256_final, .hmac_sha256_cleanup_func = signal_vala_hmac_sha256_cleanup, .sha512_digest_init_func = signal_vala_sha512_digest_init, .sha512_digest_update_func = signal_vala_sha512_digest_update, .sha512_digest_final_func = signal_vala_sha512_digest_final, .sha512_digest_cleanup_func = signal_vala_sha512_digest_cleanup, .encrypt_func = signal_vala_encrypt, .decrypt_func = signal_vala_decrypt, .user_data = 0 }; signal_context_set_crypto_provider(context, &provider); } dino-0.4.3/plugins/signal-protocol/src/signal_helper.h0000644000000000000000000000426614452563620021535 0ustar rootroot#ifndef SIGNAL_PROTOCOL_VALA_HELPER #define SIGNAL_PROTOCOL_VALA_HELPER 1 #include #include #include #define SG_CIPHER_AES_GCM_NOPADDING 1000 signal_type_base* signal_type_ref_vapi(void* what); signal_type_base* signal_type_unref_vapi(void* what); signal_protocol_address* signal_protocol_address_new(const gchar* name, int32_t device_id); void signal_protocol_address_free(signal_protocol_address* ptr); void signal_protocol_address_set_name(signal_protocol_address* self, const gchar* name); gchar* signal_protocol_address_get_name(signal_protocol_address* self); void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_t device_id); int32_t signal_protocol_address_get_device_id(signal_protocol_address* self); int signal_vala_randomize(uint8_t *data, size_t len); int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data); int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data); int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data); int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data); void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data); int signal_vala_sha512_digest_init(void **digest_context, void *user_data); int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data); int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data); void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data); int signal_vala_encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data); int signal_vala_decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data); void setup_signal_vala_crypto_provider(signal_context *context); #endif dino-0.4.3/plugins/signal-protocol/src/simple_iks.vala0000644000000000000000000000355214452563620021551 0ustar rootrootusing Gee; namespace Signal { public class SimpleIdentityKeyStore : IdentityKeyStore { public override Bytes identity_key_private { get; set; } public override Bytes identity_key_public { get; set; } public override uint32 local_registration_id { get; set; } private Map> trusted_identities = new HashMap>(); public override void save_identity(Address address, uint8[] key) throws Error { string name = address.name; if (trusted_identities.has_key(name)) { if (trusted_identities[name].has_key(address.device_id)) { trusted_identities[name][address.device_id].key = key; trusted_identity_updated(trusted_identities[name][address.device_id]); } else { trusted_identities[name][address.device_id] = new TrustedIdentity.by_address(address, key); trusted_identity_added(trusted_identities[name][address.device_id]); } } else { trusted_identities[name] = new HashMap(); trusted_identities[name][address.device_id] = new TrustedIdentity.by_address(address, key); trusted_identity_added(trusted_identities[name][address.device_id]); } } public override bool is_trusted_identity(Address address, uint8[] key) throws Error { if (!trusted_identities.has_key(address.name)) return true; if (!trusted_identities[address.name].has_key(address.device_id)) return true; uint8[] other_key = trusted_identities[address.name][address.device_id].key; if (other_key.length != key.length) return false; for (int i = 0; i < key.length; i++) { if (other_key[i] != key[i]) return false; } return true; } } } dino-0.4.3/plugins/signal-protocol/src/simple_pks.vala0000644000000000000000000000166514452563620021563 0ustar rootrootusing Gee; namespace Signal { public class SimplePreKeyStore : PreKeyStore { private Map pre_key_map = new HashMap(); public override uint8[]? load_pre_key(uint32 pre_key_id) throws Error { if (contains_pre_key(pre_key_id)) { return pre_key_map[pre_key_id].record; } return null; } public override void store_pre_key(uint32 pre_key_id, uint8[] record) throws Error { PreKeyStore.Key key = new Key(pre_key_id, record); pre_key_map[pre_key_id] = key; pre_key_stored(key); } public override bool contains_pre_key(uint32 pre_key_id) throws Error { return pre_key_map.has_key(pre_key_id); } public override void delete_pre_key(uint32 pre_key_id) throws Error { PreKeyStore.Key key; if (pre_key_map.unset(pre_key_id, out key)) { pre_key_deleted(key); } } } }dino-0.4.3/plugins/signal-protocol/src/simple_spks.vala0000644000000000000000000000201214452563620021731 0ustar rootrootusing Gee; namespace Signal { public class SimpleSignedPreKeyStore : SignedPreKeyStore { private Map pre_key_map = new HashMap(); public override uint8[]? load_signed_pre_key(uint32 pre_key_id) throws Error { if (contains_signed_pre_key(pre_key_id)) { return pre_key_map[pre_key_id].record; } return null; } public override void store_signed_pre_key(uint32 pre_key_id, uint8[] record) throws Error { SignedPreKeyStore.Key key = new Key(pre_key_id, record); pre_key_map[pre_key_id] = key; signed_pre_key_stored(key); } public override bool contains_signed_pre_key(uint32 pre_key_id) throws Error { return pre_key_map.has_key(pre_key_id); } public override void delete_signed_pre_key(uint32 pre_key_id) throws Error { SignedPreKeyStore.Key key; if (pre_key_map.unset(pre_key_id, out key)) { signed_pre_key_deleted(key); } } } }dino-0.4.3/plugins/signal-protocol/src/simple_ss.vala0000644000000000000000000000530014452563620021401 0ustar rootrootusing Gee; namespace Signal { public class SimpleSessionStore : SessionStore { private Map> session_map = new HashMap>(); public override uint8[]? load_session(Address address) throws Error { if (session_map.has_key(address.name)) { foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) return session.record; } } return null; } public override IntList get_sub_device_sessions(string name) throws Error { IntList res = new IntList(); if (session_map.has_key(name)) { foreach (SessionStore.Session session in session_map[name]) { res.add(session.device_id); } } return res; } public override void store_session(Address address, uint8[] record) throws Error { if (contains_session(address)) { delete_session(address); } if (!session_map.has_key(address.name)) { session_map[address.name] = new ArrayList(); } SessionStore.Session session = new Session() { name = address.name, device_id = address.device_id, record = record }; session_map[address.name].add(session); session_stored(session); } public override bool contains_session(Address address) throws Error { if (!session_map.has_key(address.name)) return false; foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) return true; } return false; } public override void delete_session(Address address) throws Error { if (!session_map.has_key(address.name)) throw_by_code(ErrorCode.UNKNOWN, "No session found"); foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) { session_map[address.name].remove(session); if (session_map[address.name].size == 0) { session_map.unset(address.name); } session_removed(session); return; } } } public override void delete_all_sessions(string name) throws Error { if (session_map.has_key(name)) { foreach (SessionStore.Session session in session_map[name]) { session_map[name].remove(session); if (session_map[name].size == 0) { session_map.unset(name); } session_removed(session); } } } } }dino-0.4.3/plugins/signal-protocol/src/store.vala0000644000000000000000000003516114452563620020547 0ustar rootrootnamespace Signal { public abstract class IdentityKeyStore : Object { public abstract Bytes identity_key_private { get; set; } public abstract Bytes identity_key_public { get; set; } public abstract uint32 local_registration_id { get; set; } public signal void trusted_identity_added(TrustedIdentity id); public signal void trusted_identity_updated(TrustedIdentity id); public abstract void save_identity(Address address, uint8[] key) throws Error ; public abstract bool is_trusted_identity(Address address, uint8[] key) throws Error ; public class TrustedIdentity { public uint8[] key { get; set; } public string name { get; private set; } public int device_id { get; private set; } public TrustedIdentity(string name, int device_id, uint8[] key) { this.key = key; this.name = name; this.device_id = device_id; } public TrustedIdentity.by_address(Address address, uint8[] key) { this(address.name, address.device_id, key); } } } public abstract class SessionStore : Object { public signal void session_stored(Session session); public signal void session_removed(Session session); public abstract uint8[]? load_session(Address address) throws Error ; public abstract IntList get_sub_device_sessions(string name) throws Error ; public abstract void store_session(Address address, uint8[] record) throws Error ; public abstract bool contains_session(Address address) throws Error ; public abstract void delete_session(Address address) throws Error ; public abstract void delete_all_sessions(string name) throws Error ; public class Session { public string name; public int device_id; public uint8[] record; } } public abstract class PreKeyStore : Object { public signal void pre_key_stored(Key key); public signal void pre_key_deleted(Key key); public abstract uint8[]? load_pre_key(uint32 pre_key_id) throws Error ; public abstract void store_pre_key(uint32 pre_key_id, uint8[] record) throws Error ; public abstract bool contains_pre_key(uint32 pre_key_id) throws Error ; public abstract void delete_pre_key(uint32 pre_key_id) throws Error ; public class Key { public uint32 key_id { get; private set; } public uint8[] record { get; private set; } public Key(uint32 key_id, uint8[] record) { this.key_id = key_id; this.record = record; } } } public abstract class SignedPreKeyStore : Object { public signal void signed_pre_key_stored(Key key); public signal void signed_pre_key_deleted(Key key); public abstract uint8[]? load_signed_pre_key(uint32 pre_key_id) throws Error ; public abstract void store_signed_pre_key(uint32 pre_key_id, uint8[] record) throws Error ; public abstract bool contains_signed_pre_key(uint32 pre_key_id) throws Error ; public abstract void delete_signed_pre_key(uint32 pre_key_id) throws Error ; public class Key { public uint32 key_id { get; private set; } public uint8[] record { get; private set; } public Key(uint32 key_id, uint8[] record) { this.key_id = key_id; this.record = record; } } } public class Store : Object { public Context context { get; private set; } public IdentityKeyStore identity_key_store { get; set; default = new SimpleIdentityKeyStore(); } public SessionStore session_store { get; set; default = new SimpleSessionStore(); } public PreKeyStore pre_key_store { get; set; default = new SimplePreKeyStore(); } public SignedPreKeyStore signed_pre_key_store { get; set; default = new SimpleSignedPreKeyStore(); } public uint32 local_registration_id { get { return identity_key_store.local_registration_id; } } internal NativeStoreContext native_context {get { return native_store_context_; }} private NativeStoreContext native_store_context_; static int iks_get_identity_key_pair(out Buffer public_data, out Buffer private_data, void* user_data) { Store store = (Store) user_data; public_data = new Buffer.from(store.identity_key_store.identity_key_public.get_data()); private_data = new Buffer.from(store.identity_key_store.identity_key_private.get_data()); return 0; } static int iks_get_local_registration_id(void* user_data, out uint32 registration_id) { Store store = (Store) user_data; registration_id = store.identity_key_store.local_registration_id; return 0; } static int iks_save_identity(Address address, uint8[] key, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.identity_key_store.save_identity(address, key); return 0; }); } static int iks_is_trusted_identity(Address address, uint8[] key, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.identity_key_store.is_trusted_identity(address, key) ? 1 : 0; }); } static void iks_destroy_func(void* user_data) { } static int ss_load_session_func(out Buffer? record, out Buffer? user_record, Address address, void* user_data) { Store store = (Store) user_data; user_record = null; // No support for user_record uint8[]? res = null; try { res = store.session_store.load_session(address); } catch (Error e) { record = null; return e.code; } if (res == null) { record = null; return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int ss_get_sub_device_sessions_func(out IntList? sessions, char[] name, void* user_data) { Store store = (Store) user_data; try { sessions = store.session_store.get_sub_device_sessions(carr_to_string(name)); } catch (Error e) { sessions = null; return e.code; } return 0; } static int ss_store_session_func(Address address, uint8[] record, uint8[] user_record, void* user_data) { // Ignoring user_record Store store = (Store) user_data; return catch_to_code(() => { store.session_store.store_session(address, record); return 0; }); } static int ss_contains_session_func(Address address, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.session_store.contains_session(address) ? 1 : 0; }); } static int ss_delete_session_func(Address address, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.session_store.delete_session(address); return 0; }); } static int ss_delete_all_sessions_func(char[] name, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.session_store.delete_all_sessions(carr_to_string(name)); return 0; }); } static void ss_destroy_func(void* user_data) { } static int pks_load_pre_key(out Buffer? record, uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; uint8[]? res = null; try { res = store.pre_key_store.load_pre_key(pre_key_id); } catch (Error e) { record = null; return e.code; } if (res == null) { record = new Buffer(0); return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int pks_store_pre_key(uint32 pre_key_id, uint8[] record, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.pre_key_store.store_pre_key(pre_key_id, record); return 0; }); } static int pks_contains_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.pre_key_store.contains_pre_key(pre_key_id) ? 1 : 0; }); } static int pks_remove_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.pre_key_store.delete_pre_key(pre_key_id); return 0; }); } static void pks_destroy_func(void* user_data) { } static int spks_load_signed_pre_key(out Buffer? record, uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; uint8[]? res = null; try { res = store.signed_pre_key_store.load_signed_pre_key(pre_key_id); } catch (Error e) { record = null; return e.code; } if (res == null) { record = new Buffer(0); return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int spks_store_signed_pre_key(uint32 pre_key_id, uint8[] record, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.signed_pre_key_store.store_signed_pre_key(pre_key_id, record); return 0; }); } static int spks_contains_signed_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.signed_pre_key_store.contains_signed_pre_key(pre_key_id) ? 1 : 0; }); } static int spks_remove_signed_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.signed_pre_key_store.delete_signed_pre_key(pre_key_id); return 0; }); } static void spks_destroy_func(void* user_data) { } internal Store(Context context) { this.context = context; NativeStoreContext.create(out native_store_context_, context.native_context); NativeIdentityKeyStore iks = NativeIdentityKeyStore() { get_identity_key_pair = iks_get_identity_key_pair, get_local_registration_id = iks_get_local_registration_id, save_identity = iks_save_identity, is_trusted_identity = iks_is_trusted_identity, destroy_func = iks_destroy_func, user_data = this }; native_context.set_identity_key_store(iks); NativeSessionStore ss = NativeSessionStore() { load_session_func = ss_load_session_func, get_sub_device_sessions_func = ss_get_sub_device_sessions_func, store_session_func = ss_store_session_func, contains_session_func = ss_contains_session_func, delete_session_func = ss_delete_session_func, delete_all_sessions_func = ss_delete_all_sessions_func, destroy_func = ss_destroy_func, user_data = this }; native_context.set_session_store(ss); NativePreKeyStore pks = NativePreKeyStore() { load_pre_key = pks_load_pre_key, store_pre_key = pks_store_pre_key, contains_pre_key = pks_contains_pre_key, remove_pre_key = pks_remove_pre_key, destroy_func = pks_destroy_func, user_data = this }; native_context.set_pre_key_store(pks); NativeSignedPreKeyStore spks = NativeSignedPreKeyStore() { load_signed_pre_key = spks_load_signed_pre_key, store_signed_pre_key = spks_store_signed_pre_key, contains_signed_pre_key = spks_contains_signed_pre_key, remove_signed_pre_key = spks_remove_signed_pre_key, destroy_func = spks_destroy_func, user_data = this }; native_context.set_signed_pre_key_store(spks); } public SessionBuilder create_session_builder(Address other) throws Error { SessionBuilder builder; throw_by_code(session_builder_create(out builder, native_context, other, context.native_context), "Error creating session builder"); return builder; } public SessionCipher create_session_cipher(Address other) throws Error { SessionCipher cipher; throw_by_code(session_cipher_create(out cipher, native_context, other, context.native_context)); return cipher; } public IdentityKeyPair identity_key_pair { owned get { IdentityKeyPair pair; Protocol.Identity.get_key_pair(native_context, out pair); return pair; } } public bool is_trusted_identity(Address address, ECPublicKey key) throws Error { return throw_by_code(Protocol.Identity.is_trusted_identity(native_context, address, key)) == 1; } public void save_identity(Address address, ECPublicKey key) throws Error { throw_by_code(Protocol.Identity.save_identity(native_context, address, key)); } public bool contains_session(Address other) throws Error { return throw_by_code(Protocol.Session.contains_session(native_context, other)) == 1; } public void delete_session(Address address) throws Error { throw_by_code(Protocol.Session.delete_session(native_context, address)); } public SessionRecord load_session(Address other) throws Error { SessionRecord record; throw_by_code(Protocol.Session.load_session(native_context, out record, other)); return record; } public bool contains_pre_key(uint32 pre_key_id) throws Error { return throw_by_code(Protocol.PreKey.contains_key(native_context, pre_key_id)) == 1; } public void store_pre_key(PreKeyRecord record) throws Error { throw_by_code(Protocol.PreKey.store_key(native_context, record)); } public PreKeyRecord load_pre_key(uint32 pre_key_id) throws Error { PreKeyRecord res; throw_by_code(Protocol.PreKey.load_key(native_context, out res, pre_key_id)); return res; } public bool contains_signed_pre_key(uint32 pre_key_id) throws Error { return throw_by_code(Protocol.SignedPreKey.contains_key(native_context, pre_key_id)) == 1; } public void store_signed_pre_key(SignedPreKeyRecord record) throws Error { throw_by_code(Protocol.SignedPreKey.store_key(native_context, record)); } public SignedPreKeyRecord load_signed_pre_key(uint32 pre_key_id) throws Error { SignedPreKeyRecord res; throw_by_code(Protocol.SignedPreKey.load_key(native_context, out res, pre_key_id)); return res; } } } dino-0.4.3/plugins/signal-protocol/src/util.vala0000644000000000000000000000313514452563620020364 0ustar rootrootnamespace Signal { public ECPublicKey generate_public_key(ECPrivateKey private_key) throws Error { ECPublicKey public_key; throw_by_code(ECPublicKey.generate(out public_key, private_key), "Error generating public key"); return public_key; } public uint8[] calculate_agreement(ECPublicKey public_key, ECPrivateKey private_key) throws Error { uint8[] res; int len = Curve.calculate_agreement(out res, public_key, private_key); throw_by_code(len, "Error calculating agreement"); res.length = len; return res; } public bool verify_signature(ECPublicKey signing_key, uint8[] message, uint8[] signature) throws Error { return throw_by_code(Curve.verify_signature(signing_key, message, signature)) == 1; } public PreKeyBundle create_pre_key_bundle(uint32 registration_id, int device_id, uint32 pre_key_id, ECPublicKey? pre_key_public, uint32 signed_pre_key_id, ECPublicKey? signed_pre_key_public, uint8[]? signed_pre_key_signature, ECPublicKey? identity_key) throws Error { PreKeyBundle res; throw_by_code(PreKeyBundle.create(out res, registration_id, device_id, pre_key_id, pre_key_public, signed_pre_key_id, signed_pre_key_public, signed_pre_key_signature, identity_key), "Error creating PreKeyBundle"); return res; } internal string carr_to_string(char[] carr) { char[] nu = new char[carr.length + 1]; Memory.copy(nu, carr, carr.length); return (string) nu; } internal delegate int CodeErroringFunc() throws Error; internal int catch_to_code(CodeErroringFunc func) { try { return func(); } catch (Error e) { return e.code; } } }dino-0.4.3/plugins/signal-protocol/tests/0000755000000000000000000000000014452563620017113 5ustar rootrootdino-0.4.3/plugins/signal-protocol/tests/common.vala0000644000000000000000000000551314452563620021254 0ustar rootrootnamespace Signal.Test { int main(string[] args) { GLib.Test.init(ref args); GLib.Test.set_nonfatal_assertions(); TestSuite.get_root().add_suite(new Curve25519().get_suite()); TestSuite.get_root().add_suite(new SessionBuilderTest().get_suite()); TestSuite.get_root().add_suite(new HKDF().get_suite()); return GLib.Test.run(); } Store setup_test_store_context(Context global_context) { Store store = global_context.create_store(); try { store.identity_key_store.local_registration_id = (Random.next_int() % 16380) + 1; ECKeyPair key_pair = global_context.generate_key_pair(); store.identity_key_store.identity_key_private = new Bytes(key_pair.private.serialize()); store.identity_key_store.identity_key_public = new Bytes(key_pair.public.serialize()); } catch (Error e) { fail_if_reached(); } return store; } ECPublicKey? create_test_ec_public_key(Context context) { try { return context.generate_key_pair().public; } catch (Error e) { fail_if_reached(); return null; } } bool fail_if(bool exp, string? reason = null) { if (exp) { if (reason != null) GLib.Test.message(reason); GLib.Test.fail(); return true; } return false; } void fail_if_reached(string? reason = null) { fail_if(true, reason); } delegate void ErrorFunc() throws Error; void fail_if_not_error_code(ErrorFunc func, int expectedCode, string? reason = null) { try { func(); fail_if_reached(@"$(reason + ": " ?? "")no error thrown"); } catch (Error e) { fail_if_not_eq_int(e.code, expectedCode, @"$(reason + ": " ?? "")caught unexpected error"); } } bool fail_if_not(bool exp, string? reason = null) { return fail_if(!exp, reason); } bool fail_if_eq_int(int left, int right, string? reason = null) { return fail_if(left == right, @"$(reason + ": " ?? "")$left == $right"); } bool fail_if_not_eq_int(int left, int right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_str(string left, string right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_uint8_arr(uint8[] left, uint8[] right, string? reason = null) { if (fail_if_not_eq_int(left.length, right.length, @"$(reason + ": " ?? "")array length not equal")) return true; return fail_if_not_eq_str(Base64.encode(left), Base64.encode(right), reason); } bool fail_if_not_zero_int(int zero, string? reason = null) { return fail_if_not_eq_int(zero, 0, reason); } bool fail_if_zero_int(int zero, string? reason = null) { return fail_if_eq_int(zero, 0, reason); } bool fail_if_null(void* what, string? reason = null) { return fail_if(what == null || (size_t)what == 0, reason); } } dino-0.4.3/plugins/signal-protocol/tests/curve25519.vala0000644000000000000000000002012314452563620021510 0ustar rootrootnamespace Signal.Test { class Curve25519 : Gee.TestCase { public Curve25519() { base("Curve25519"); add_test("agreement", test_curve25519_agreement); add_test("generate_public", test_curve25519_generate_public); add_test("random_agreements", test_curve25519_random_agreements); add_test("signature", test_curve25519_signature); } private Context global_context; public override void set_up() { try { global_context = new Context(); } catch (Error e) { fail_if_reached(); } } public override void tear_down() { global_context = null; } void test_curve25519_agreement() { try { uint8[] alicePublic = { 0x05, 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28}; uint8[] alicePrivate = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59}; uint8[] bobPublic = { 0x05, 0x65, 0x36, 0x14, 0x99, 0x3d, 0x2b, 0x15, 0xee, 0x9e, 0x5f, 0xd3, 0xd8, 0x6c, 0xe7, 0x19, 0xef, 0x4e, 0xc1, 0xda, 0xae, 0x18, 0x86, 0xa8, 0x7b, 0x3f, 0x5f, 0xa9, 0x56, 0x5a, 0x27, 0xa2, 0x2f}; uint8[] bobPrivate = { 0xb0, 0x3b, 0x34, 0xc3, 0x3a, 0x1c, 0x44, 0xf2, 0x25, 0xb6, 0x62, 0xd2, 0xbf, 0x48, 0x59, 0xb8, 0x13, 0x54, 0x11, 0xfa, 0x7b, 0x03, 0x86, 0xd4, 0x5f, 0xb7, 0x5d, 0xc5, 0xb9, 0x1b, 0x44, 0x66}; uint8[] shared = { 0x32, 0x5f, 0x23, 0x93, 0x28, 0x94, 0x1c, 0xed, 0x6e, 0x67, 0x3b, 0x86, 0xba, 0x41, 0x01, 0x74, 0x48, 0xe9, 0x9b, 0x64, 0x9a, 0x9c, 0x38, 0x06, 0xc1, 0xdd, 0x7c, 0xa4, 0xc4, 0x77, 0xe6, 0x29}; ECPublicKey alice_public_key = global_context.decode_public_key(alicePublic); ECPrivateKey alice_private_key = global_context.decode_private_key(alicePrivate); ECPublicKey bob_public_key = global_context.decode_public_key(bobPublic); ECPrivateKey bob_private_key = global_context.decode_private_key(bobPrivate); uint8[] shared_one = calculate_agreement(alice_public_key, bob_private_key); uint8[] shared_two = calculate_agreement(bob_public_key, alice_private_key); fail_if_not_eq_int(shared_one.length, 32); fail_if_not_eq_int(shared_two.length, 32); fail_if_not_eq_uint8_arr(shared, shared_one); fail_if_not_eq_uint8_arr(shared_one, shared_two); } catch (Error e) { fail_if_reached(); } } void test_curve25519_generate_public() { try { uint8[] alicePublic = { 0x05, 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28}; uint8[] alicePrivate = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59}; ECPrivateKey alice_private_key = global_context.decode_private_key(alicePrivate); ECPublicKey alice_expected_public_key = global_context.decode_public_key(alicePublic); ECPublicKey alice_public_key = generate_public_key(alice_private_key); fail_if_not_zero_int(alice_expected_public_key.compare(alice_public_key)); } catch (Error e) { fail_if_reached(); } } void test_curve25519_random_agreements() { try { ECKeyPair alice_key_pair = null; ECPublicKey alice_public_key = null; ECPrivateKey alice_private_key = null; ECKeyPair bob_key_pair = null; ECPublicKey bob_public_key = null; ECPrivateKey bob_private_key = null; uint8[] shared_alice = null; uint8[] shared_bob = null; for (int i = 0; i < 50; i++) { fail_if_null(alice_key_pair = global_context.generate_key_pair()); fail_if_null(alice_public_key = alice_key_pair.public); fail_if_null(alice_private_key = alice_key_pair.private); fail_if_null(bob_key_pair = global_context.generate_key_pair()); fail_if_null(bob_public_key = bob_key_pair.public); fail_if_null(bob_private_key = bob_key_pair.private); shared_alice = calculate_agreement(bob_public_key, alice_private_key); fail_if_not_eq_int(shared_alice.length, 32); shared_bob = calculate_agreement(alice_public_key, bob_private_key); fail_if_not_eq_int(shared_bob.length, 32); fail_if_not_eq_uint8_arr(shared_alice, shared_bob); } } catch (Error e) { fail_if_reached(); } } void test_curve25519_signature() { try { uint8[] aliceIdentityPrivate = { 0xc0, 0x97, 0x24, 0x84, 0x12, 0xe5, 0x8b, 0xf0, 0x5d, 0xf4, 0x87, 0x96, 0x82, 0x05, 0x13, 0x27, 0x94, 0x17, 0x8e, 0x36, 0x76, 0x37, 0xf5, 0x81, 0x8f, 0x81, 0xe0, 0xe6, 0xce, 0x73, 0xe8, 0x65}; uint8[] aliceIdentityPublic = { 0x05, 0xab, 0x7e, 0x71, 0x7d, 0x4a, 0x16, 0x3b, 0x7d, 0x9a, 0x1d, 0x80, 0x71, 0xdf, 0xe9, 0xdc, 0xf8, 0xcd, 0xcd, 0x1c, 0xea, 0x33, 0x39, 0xb6, 0x35, 0x6b, 0xe8, 0x4d, 0x88, 0x7e, 0x32, 0x2c, 0x64}; uint8[] aliceEphemeralPublic = { 0x05, 0xed, 0xce, 0x9d, 0x9c, 0x41, 0x5c, 0xa7, 0x8c, 0xb7, 0x25, 0x2e, 0x72, 0xc2, 0xc4, 0xa5, 0x54, 0xd3, 0xeb, 0x29, 0x48, 0x5a, 0x0e, 0x1d, 0x50, 0x31, 0x18, 0xd1, 0xa8, 0x2d, 0x99, 0xfb, 0x4a}; uint8[] aliceSignature = { 0x5d, 0xe8, 0x8c, 0xa9, 0xa8, 0x9b, 0x4a, 0x11, 0x5d, 0xa7, 0x91, 0x09, 0xc6, 0x7c, 0x9c, 0x74, 0x64, 0xa3, 0xe4, 0x18, 0x02, 0x74, 0xf1, 0xcb, 0x8c, 0x63, 0xc2, 0x98, 0x4e, 0x28, 0x6d, 0xfb, 0xed, 0xe8, 0x2d, 0xeb, 0x9d, 0xcd, 0x9f, 0xae, 0x0b, 0xfb, 0xb8, 0x21, 0x56, 0x9b, 0x3d, 0x90, 0x01, 0xbd, 0x81, 0x30, 0xcd, 0x11, 0xd4, 0x86, 0xce, 0xf0, 0x47, 0xbd, 0x60, 0xb8, 0x6e, 0x88}; global_context.decode_private_key(aliceIdentityPrivate); global_context.decode_public_key(aliceEphemeralPublic); ECPublicKey alice_public_key = global_context.decode_public_key(aliceIdentityPublic); fail_if(!verify_signature(alice_public_key, aliceEphemeralPublic, aliceSignature), "signature verification failed"); uint8[] modifiedSignature = new uint8[aliceSignature.length]; for (int i = 0; i < aliceSignature.length; i++) { Memory.copy(modifiedSignature, aliceSignature, aliceSignature.length); modifiedSignature[i] ^= 0x01; fail_if(verify_signature(alice_public_key, aliceEphemeralPublic, modifiedSignature), "invalid signature verification succeeded"); } } catch (Error e) { fail_if_reached(); } } } }dino-0.4.3/plugins/signal-protocol/tests/hkdf.vala0000644000000000000000000000326314452563620020700 0ustar rootrootnamespace Signal.Test { class HKDF : Gee.TestCase { public HKDF() { base("HKDF"); add_test("vector_v3", test_hkdf_vector_v3); } private Context global_context; public override void set_up() { try { global_context = new Context(); } catch (Error e) { fail_if_reached(); } } public override void tear_down() { global_context = null; } public void test_hkdf_vector_v3() { uint8[] ikm = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; uint8[] salt = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}; uint8[] info = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; uint8[] okm = { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}; NativeHkdfContext context = null; fail_if_not_zero_int(NativeHkdfContext.create(out context, 3, global_context.native_context)); uint8[] output = null; int result = (int) context.derive_secrets(out output, ikm, salt, info, 42); fail_if_not_eq_int(result, okm.length); output.length = result; fail_if_not_eq_uint8_arr(output, okm); } } }dino-0.4.3/plugins/signal-protocol/tests/session_builder.vala0000644000000000000000000005070614452563620023161 0ustar rootrootnamespace Signal.Test { class SessionBuilderTest : Gee.TestCase { Address alice_address; Address bob_address; public SessionBuilderTest() { base("SessionBuilder"); add_test("basic_pre_key_v2", test_basic_pre_key_v2); add_test("basic_pre_key_v3", test_basic_pre_key_v3); add_test("bad_signed_pre_key_signature", test_bad_signed_pre_key_signature); add_test("repeat_bundle_message_v2", test_repeat_bundle_message_v2); } private Context global_context; public override void set_up() { try { global_context = new Context(); alice_address = new Address("+14151111111", 1); bob_address = new Address("+14152222222", 1); } catch (Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } public override void tear_down() { global_context = null; alice_address = null; bob_address = null; } void test_basic_pre_key_v2() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); uint32 bob_local_registration_id = bob_store.local_registration_id; IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, bob_pre_key_pair.public, 0, null, null, bob_identity_key_pair.public); /* * Have Alice process Bob's pre key bundle, which should fail due to a * missing unsigned pre key. */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_basic_pre_key_v3() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); uint32 bob_local_registration_id = bob_store.local_registration_id; ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, bob_pre_key_pair.public, 22, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Check that we can load the session state and verify its version */ fail_if_not(alice_store.contains_session(bob_address)); SessionRecord loaded_record = alice_store.load_session(bob_address); fail_if_not_eq_int((int)loaded_record.state.session_version, 3); /* Encrypt an outgoing message to send to Bob */ string original_message = "L'homme est condamné à être libre"; SessionCipher alice_session_cipher = alice_store.create_session_cipher(bob_address); CiphertextMessage outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Convert to an incoming message for Bob */ PreKeySignalMessage incoming_message = global_context.deserialize_pre_key_signal_message(outgoing_message.serialized); /* Save the pre key and signed pre key in Bob's data store */ PreKeyRecord bob_pre_key_record; throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); SignedPreKeyRecord bob_signed_pre_key_record; throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 22, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Create Bob's session cipher and decrypt the message from Alice */ SessionCipher bob_session_cipher = bob_store.create_session_cipher(alice_address); /* Prepare the data for the callback test */ //int callback_context = 1234; //bob_session_cipher.user_data = //bob_session_cipher.decryption_callback = uint8[] plaintext = bob_session_cipher.decrypt_pre_key_signal_message(incoming_message); /* Clean up callback data */ bob_session_cipher.user_data = null; bob_session_cipher.decryption_callback = null; /* Verify Bob's session state and the decrypted message */ fail_if_not(bob_store.contains_session(alice_address)); SessionRecord alice_recipient_session_record = bob_store.load_session(alice_address); SessionState alice_recipient_session_state = alice_recipient_session_record.state; fail_if_not_eq_int((int)alice_recipient_session_state.session_version, 3); fail_if_null(alice_recipient_session_state.alice_base_key); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Have Bob send a reply to Alice */ CiphertextMessage bob_outgoing_message = bob_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(bob_outgoing_message.type, CiphertextType.SIGNAL); /* Verify that Alice can decrypt it */ SignalMessage bob_outgoing_message_copy = global_context.copy_signal_message(bob_outgoing_message); uint8[] alice_plaintext = alice_session_cipher.decrypt_signal_message(bob_outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, alice_plaintext); GLib.Test.message("Pre-interaction tests complete"); /* Interaction tests */ run_interaction(alice_store, bob_store); /* Cleanup state from previous tests that we need to replace */ alice_store = null; bob_pre_key_pair = null; bob_signed_pre_key_pair = null; bob_identity_key_pair = null; bob_signed_pre_key_signature = null; bob_pre_key_record = null; bob_signed_pre_key_record = null; /* Create Alice's new session data */ alice_store = setup_test_store_context(global_context); alice_session_builder = alice_store.create_session_builder(bob_address); alice_session_cipher = alice_store.create_session_cipher(bob_address); /* Create Bob's new pre key bundle */ bob_pre_key_pair = global_context.generate_key_pair(); bob_signed_pre_key_pair = global_context.generate_key_pair(); bob_identity_key_pair = bob_store.identity_key_pair; bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31338, bob_pre_key_pair.public, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Save the new pre key and signed pre key in Bob's data store */ throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 23, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Have Alice encrypt a message for Bob */ outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Have Bob try to decrypt the message */ PreKeySignalMessage outgoing_message_copy = global_context.copy_pre_key_signal_message(outgoing_message); /* The decrypt should fail with a specific error */ fail_if_not_error_code(() => bob_session_cipher.decrypt_pre_key_signal_message(outgoing_message_copy), ErrorCode.UNTRUSTED_IDENTITY); outgoing_message_copy = global_context.copy_pre_key_signal_message(outgoing_message); /* Save the identity key to Bob's store */ bob_store.save_identity(alice_address, outgoing_message_copy.identity_key); /* Try the decrypt again, this time it should succeed */ outgoing_message_copy = global_context.copy_pre_key_signal_message(outgoing_message); plaintext = bob_session_cipher.decrypt_pre_key_signal_message(outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Create a new pre key for Bob */ ECPublicKey test_public_key = create_test_ec_public_key(global_context); IdentityKeyPair alice_identity_key_pair = alice_store.identity_key_pair; bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, test_public_key, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, alice_identity_key_pair.public); /* Have Alice process Bob's new pre key bundle, which should fail */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.UNTRUSTED_IDENTITY); GLib.Test.message("Post-interaction tests complete"); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_bad_signed_pre_key_signature() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store */ Store bob_store = setup_test_store_context(global_context); /* Create Bob's regular and signed pre key pairs */ ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); /* Create Bob's signed pre key signature */ IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); for (int i = 0; i < bob_signed_pre_key_signature.length * 8; i++) { uint8[] modified_signature = bob_signed_pre_key_signature[0:bob_signed_pre_key_signature.length]; /* Intentionally corrupt the signature data */ modified_signature[i/8] ^= (1 << ((uint8)i % 8)); /* Create a pre key bundle */ PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31137,bob_pre_key_pair.public,22,bob_signed_pre_key_pair.public,modified_signature,bob_identity_key_pair.public); /* Process the bundle and make sure we fail with an invalid key error */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } /* Create a correct pre key bundle */ PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31137,bob_pre_key_pair.public,22,bob_signed_pre_key_pair.public,bob_signed_pre_key_signature,bob_identity_key_pair.public); /* Process the bundle and make sure we do not fail */ alice_session_builder.process_pre_key_bundle(bob_pre_key); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_repeat_bundle_message_v2() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_store.identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31337,bob_pre_key_pair.public,0,null,null,bob_store.identity_key_pair.public); /* Add Bob's pre keys to Bob's data store */ PreKeyRecord bob_pre_key_record; throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); SignedPreKeyRecord bob_signed_pre_key_record; throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 22, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* * Have Alice process Bob's pre key bundle, which should fail due to a * missing signed pre key. */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } class Holder { public uint8[] data { get; private set; } public Holder(uint8[] data) { this.data = data; } } void run_interaction(Store alice_store, Store bob_store) throws Error { /* Create the session ciphers */ SessionCipher alice_session_cipher = alice_store.create_session_cipher(bob_address); SessionCipher bob_session_cipher = bob_store.create_session_cipher(alice_address); /* Create a test message */ string original_message = "smert ze smert"; /* Simulate Alice sending a message to Bob */ CiphertextMessage alice_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(alice_message.type, CiphertextType.SIGNAL); SignalMessage alice_message_copy = global_context.copy_signal_message(alice_message); uint8[] plaintext = bob_session_cipher.decrypt_signal_message(alice_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); GLib.Test.message("Interaction complete: Alice -> Bob"); /* Simulate Bob sending a message to Alice */ CiphertextMessage bob_message = bob_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(alice_message.type, CiphertextType.SIGNAL); SignalMessage bob_message_copy = global_context.copy_signal_message(bob_message); plaintext = alice_session_cipher.decrypt_signal_message(bob_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); GLib.Test.message("Interaction complete: Bob -> Alice"); /* Looping Alice -> Bob */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage alice_looping_message = alice_session_cipher.encrypt(looping_message); SignalMessage alice_looping_message_copy = global_context.copy_signal_message(alice_looping_message); uint8[] looping_plaintext = bob_session_cipher.decrypt_signal_message(alice_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (looping)"); /* Looping Bob -> Alice */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage bob_looping_message = bob_session_cipher.encrypt(looping_message); SignalMessage bob_looping_message_copy = global_context.copy_signal_message(bob_looping_message); uint8[] looping_plaintext = alice_session_cipher.decrypt_signal_message(bob_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Bob -> Alice (looping)"); /* Generate a shuffled list of encrypted messages for later use */ Holder[] alice_ooo_plaintext = new Holder[10]; Holder[] alice_ooo_ciphertext = new Holder[10]; for (int i = 0; i < 10; i++) { alice_ooo_plaintext[i] = new Holder(create_looping_message(i)); alice_ooo_ciphertext[i] = new Holder(alice_session_cipher.encrypt(alice_ooo_plaintext[i].data).serialized); } for (int i = 0; i < 10; i++) { uint32 s = Random.next_int() % 10; Holder tmp = alice_ooo_plaintext[s]; alice_ooo_plaintext[s] = alice_ooo_plaintext[i]; alice_ooo_plaintext[i] = tmp; tmp = alice_ooo_ciphertext[s]; alice_ooo_ciphertext[s] = alice_ooo_ciphertext[i]; alice_ooo_ciphertext[i] = tmp; } GLib.Test.message("Shuffled Alice->Bob messages created"); /* Looping Alice -> Bob (repeated) */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage alice_looping_message = alice_session_cipher.encrypt(looping_message); SignalMessage alice_looping_message_copy = global_context.copy_signal_message(alice_looping_message); uint8[] looping_plaintext = bob_session_cipher.decrypt_signal_message(alice_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (looping, repeated)"); /* Looping Bob -> Alice (repeated) */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage bob_looping_message = bob_session_cipher.encrypt(looping_message); SignalMessage bob_looping_message_copy = global_context.copy_signal_message(bob_looping_message); uint8[] looping_plaintext = alice_session_cipher.decrypt_signal_message(bob_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Bob -> Alice (looping, repeated)"); /* Shuffled Alice -> Bob */ for (int i = 0; i < 10; i++) { SignalMessage ooo_message_deserialized = global_context.deserialize_signal_message(alice_ooo_ciphertext[i].data); uint8[] ooo_plaintext = bob_session_cipher.decrypt_signal_message(ooo_message_deserialized); fail_if_not_eq_uint8_arr(alice_ooo_plaintext[i].data, ooo_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (shuffled)"); } uint8[] create_looping_message(int index) { return (@"You can only desire based on what you know: $index").data; } /* uint8[] create_looping_message_short(int index) { return ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + @"surges up in the world--and defines himself aftward. $index").data; } */ } } dino-0.4.3/plugins/signal-protocol/tests/testcase.vala0000644000000000000000000000451314452563620021576 0ustar rootroot/* testcase.vala * * Copyright (C) 2009 Julien Peeters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Julien Peeters */ public abstract class Gee.TestCase : Object { private GLib.TestSuite suite; private Adaptor[] adaptors = new Adaptor[0]; public delegate void TestMethod (); protected TestCase (string name) { this.suite = new GLib.TestSuite (name); } public void add_test (string name, owned TestMethod test) { var adaptor = new Adaptor (name, (owned)test, this); this.adaptors += adaptor; this.suite.add (new GLib.TestCase (adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down )); } public virtual void set_up () { } public virtual void tear_down () { } public GLib.TestSuite get_suite () { return (owned) this.suite; } private class Adaptor { [CCode (notify = false)] public string name { get; private set; } private TestMethod test; private TestCase test_case; public Adaptor (string name, owned TestMethod test, TestCase test_case) { this.name = name; this.test = (owned)test; this.test_case = test_case; } public void set_up (void* fixture) { this.test_case.set_up (); } public void run (void* fixture) { this.test (); } public void tear_down (void* fixture) { this.test_case.tear_down (); } } } dino-0.4.3/plugins/signal-protocol/vapi/0000755000000000000000000000000014452563620016710 5ustar rootrootdino-0.4.3/plugins/signal-protocol/vapi/signal-protocol-native.vapi0000644000000000000000000004063014452563620024174 0ustar rootrootnamespace Signal { [Compact] [CCode (cname = "signal_context", cprefix="signal_context_", free_function="signal_context_destroy", cheader_filename = "signal/signal_protocol.h")] public class NativeContext { public static int create(out NativeContext context, void* user_data); public int set_crypto_provider(NativeCryptoProvider crypto_provider); public int set_locking_functions(LockingFunc lock, LockingFunc unlock); public int set_log_function(LogFunc log); } [CCode (has_target = false)] public delegate void LockingFunc(void* user_data); [CCode (has_target = false)] public delegate void LogFunc(LogLevel level, string message, size_t len, void* user_data); [Compact] [CCode (cname = "signal_crypto_provider", cheader_filename = "signal/signal_protocol.h")] public struct NativeCryptoProvider { public RandomFunc random_func; public HmacSha256Init hmac_sha256_init_func; public HmacSha256Update hmac_sha256_update_func; public HmacSha256Final hmac_sha256_final_func; public HmacSha256Cleanup hmac_sha256_cleanup_func; public Sha512DigestInit sha512_digest_init_func; public Sha512DigestUpdate sha512_digest_update_func; public Sha512DigestFinal sha512_digest_final_func; public Sha512DigestCleanup sha512_digest_cleanup_func; public CryptFunc encrypt_func; public CryptFunc decrypt_func; public void* user_data; } [CCode (has_target = false)] public delegate int RandomFunc(uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Init(out void* hmac_context, uint8[] key, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Update(void* hmac_context, uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Final(void* hmac_context, out Buffer buffer, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Cleanup(void* hmac_context, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestInit(out void* digest_context, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestUpdate(void* digest_context, uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestFinal(void* digest_context, out Buffer buffer, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestCleanup(void* digest_context, void* user_data); [CCode (has_target = false)] public delegate int CryptFunc(out Buffer output, Cipher cipher, uint8[] key, uint8[] iv, uint8[] content, void* user_data); [Compact] [CCode (cname = "signal_protocol_session_store", cheader_filename = "signal/signal_protocol.h")] public struct NativeSessionStore { public LoadSessionFunc load_session_func; public GetSubDeviceSessionsFunc get_sub_device_sessions_func; public StoreSessionFunc store_session_func; public ContainsSessionFunc contains_session_func; public DeleteSessionFunc delete_session_func; public DeleteAllSessionsFunc delete_all_sessions_func; public DestroyFunc destroy_func; public void* user_data; } [CCode (has_target = false)] public delegate int LoadSessionFunc(out Buffer record, out Buffer user_record, Address address, void* user_data); [CCode (has_target = false)] public delegate int GetSubDeviceSessionsFunc(out IntList sessions, [CCode (array_length_type = "size_t")] char[] name, void* user_data); [CCode (has_target = false)] public delegate int StoreSessionFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] record, [CCode (array_length_type = "size_t")] uint8[] user_record, void* user_data); [CCode (has_target = false)] public delegate int ContainsSessionFunc(Address address, void* user_data); [CCode (has_target = false)] public delegate int DeleteSessionFunc(Address address, void* user_data); [CCode (has_target = false)] public delegate int DeleteAllSessionsFunc([CCode (array_length_type = "size_t")] char[] name, void* user_data); [Compact] [CCode (cname = "signal_protocol_identity_key_store", cheader_filename = "signal/signal_protocol.h")] public struct NativeIdentityKeyStore { GetIdentityKeyPairFunc get_identity_key_pair; GetLocalRegistrationIdFunc get_local_registration_id; SaveIdentityFunc save_identity; IsTrustedIdentityFunc is_trusted_identity; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int GetIdentityKeyPairFunc(out Buffer public_data, out Buffer private_data, void* user_data); [CCode (has_target = false)] public delegate int GetLocalRegistrationIdFunc(void* user_data, out uint32 registration_id); [CCode (has_target = false)] public delegate int SaveIdentityFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] key, void* user_data); [CCode (has_target = false)] public delegate int IsTrustedIdentityFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] key, void* user_data); [Compact] [CCode (cname = "signal_protocol_pre_key_store", cheader_filename = "signal/signal_protocol.h")] public struct NativePreKeyStore { LoadPreKeyFunc load_pre_key; StorePreKeyFunc store_pre_key; ContainsPreKeyFunc contains_pre_key; RemovePreKeyFunc remove_pre_key; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int LoadPreKeyFunc(out Buffer record, uint32 pre_key_id, void* user_data); [CCode (has_target = false)] public delegate int StorePreKeyFunc(uint32 pre_key_id, [CCode (array_length_type = "size_t")] uint8[] record, void* user_data); [CCode (has_target = false)] public delegate int ContainsPreKeyFunc(uint32 pre_key_id, void* user_data); [CCode (has_target = false)] public delegate int RemovePreKeyFunc(uint32 pre_key_id, void* user_data); [Compact] [CCode (cname = "signal_protocol_signed_pre_key_store", cheader_filename = "signal/signal_protocol.h")] public struct NativeSignedPreKeyStore { LoadPreKeyFunc load_signed_pre_key; StorePreKeyFunc store_signed_pre_key; ContainsPreKeyFunc contains_signed_pre_key; RemovePreKeyFunc remove_signed_pre_key; DestroyFunc destroy_func; void* user_data; } [Compact] [CCode (cname = "signal_protocol_sender_key_store")] public struct NativeSenderKeyStore { StoreSenderKeyFunc store_sender_key; LoadSenderKeyFunc load_sender_key; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int StoreSenderKeyFunc(SenderKeyName sender_key_name, [CCode (array_length_type = "size_t")] uint8[] record, [CCode (array_length_type = "size_t")] uint8[] user_record, void* user_data); [CCode (has_target = false)] public delegate int LoadSenderKeyFunc(out Buffer record, out Buffer user_record, SenderKeyName sender_key_name, void* user_data); [CCode (has_target = false)] public delegate void DestroyFunc(void* user_data); [Compact] [CCode (cname = "signal_protocol_store_context", cprefix = "signal_protocol_store_context_", free_function="signal_protocol_store_context_destroy", cheader_filename = "signal/signal_protocol.h")] public class NativeStoreContext { public static int create(out NativeStoreContext context, NativeContext global_context); public int set_session_store(NativeSessionStore store); public int set_pre_key_store(NativePreKeyStore store); public int set_signed_pre_key_store(NativeSignedPreKeyStore store); public int set_identity_key_store(NativeIdentityKeyStore store); public int set_sender_key_store(NativeSenderKeyStore store); } [CCode (cheader_filename = "signal/signal_protocol.h")] namespace Protocol { /** * Interface to the pre-key store. * These functions will use the callbacks in the provided * signal_protocol_store_context instance and operate in terms of higher level * library data structures. */ [CCode (cprefix = "signal_protocol_pre_key_")] namespace PreKey { public int load_key(NativeStoreContext context, out PreKeyRecord pre_key, uint32 pre_key_id); public int store_key(NativeStoreContext context, PreKeyRecord pre_key); public int contains_key(NativeStoreContext context, uint32 pre_key_id); public int remove_key(NativeStoreContext context, uint32 pre_key_id); } [CCode (cprefix = "signal_protocol_signed_pre_key_")] namespace SignedPreKey { public int load_key(NativeStoreContext context, out SignedPreKeyRecord pre_key, uint32 pre_key_id); public int store_key(NativeStoreContext context, SignedPreKeyRecord pre_key); public int contains_key(NativeStoreContext context, uint32 pre_key_id); public int remove_key(NativeStoreContext context, uint32 pre_key_id); } /** * Interface to the session store. * These functions will use the callbacks in the provided * signal_protocol_store_context instance and operate in terms of higher level * library data structures. */ [CCode (cprefix = "signal_protocol_session_")] namespace Session { public int load_session(NativeStoreContext context, out SessionRecord record, Address address); public int get_sub_device_sessions(NativeStoreContext context, out IntList sessions, char[] name); public int store_session(NativeStoreContext context, Address address, SessionRecord record); public int contains_session(NativeStoreContext context, Address address); public int delete_session(NativeStoreContext context, Address address); public int delete_all_sessions(NativeStoreContext context, char[] name); } namespace Identity { public int get_key_pair(NativeStoreContext store_context, out IdentityKeyPair key_pair); public int get_local_registration_id(NativeStoreContext store_context, out uint32 registration_id); public int save_identity(NativeStoreContext store_context, Address address, ECPublicKey identity_key); public int is_trusted_identity(NativeStoreContext store_context, Address address, ECPublicKey identity_key); } [CCode (cheader_filename = "signal/key_helper.h", cprefix = "signal_protocol_key_helper_")] namespace KeyHelper { [Compact] [CCode (cname = "signal_protocol_key_helper_pre_key_list_node", cprefix = "signal_protocol_key_helper_key_list_", free_function="signal_protocol_key_helper_key_list_free")] public class PreKeyListNode { public PreKeyRecord element(); public PreKeyListNode next(); } public int generate_identity_key_pair(out IdentityKeyPair key_pair, NativeContext global_context); public int generate_registration_id(out int32 registration_id, int extended_range, NativeContext global_context); public int get_random_sequence(out int value, int max, NativeContext global_context); public int generate_pre_keys(out PreKeyListNode head, uint start, uint count, NativeContext global_context); public int generate_last_resort_pre_key(out PreKeyRecord pre_key, NativeContext global_context); public int generate_signed_pre_key(out SignedPreKeyRecord signed_pre_key, IdentityKeyPair identity_key_pair, uint32 signed_pre_key_id, uint64 timestamp, NativeContext global_context); public int generate_sender_signing_key(out ECKeyPair key_pair, NativeContext global_context); public int generate_sender_key(out Buffer key_buffer, NativeContext global_context); public int generate_sender_key_id(out int32 key_id, NativeContext global_context); } } [CCode (cheader_filename = "signal/curve.h")] namespace Curve { [CCode (cname = "curve_calculate_agreement")] public int calculate_agreement([CCode (array_length = false)] out uint8[] shared_key_data, ECPublicKey public_key, ECPrivateKey private_key); [CCode (cname = "curve_calculate_signature")] public int calculate_signature(NativeContext context, out Buffer signature, ECPrivateKey signing_key, uint8[] message); [CCode (cname = "curve_verify_signature")] public int verify_signature(ECPublicKey signing_key, uint8[] message, uint8[] signature); } [CCode (cname = "session_builder_create", cheader_filename = "signal/session_builder.h")] public static int session_builder_create(out SessionBuilder builder, NativeStoreContext store, Address remote_address, NativeContext global_context); [CCode (cname = "session_cipher_create", cheader_filename = "signal/session_cipher.h")] public static int session_cipher_create(out SessionCipher cipher, NativeStoreContext store, Address remote_address, NativeContext global_context); [CCode (cname = "pre_key_signal_message_deserialize", cheader_filename = "signal/protocol.h")] public static int pre_key_signal_message_deserialize(out PreKeySignalMessage message, uint8[] data, NativeContext global_context); [CCode (cname = "pre_key_signal_message_copy", cheader_filename = "signal/protocol.h")] public static int pre_key_signal_message_copy(out PreKeySignalMessage message, PreKeySignalMessage other_message, NativeContext global_context); [CCode (cname = "signal_message_create", cheader_filename = "signal/protocol.h")] public static int signal_message_create(out SignalMessage message, uint8 message_version, uint8[] mac_key, ECPublicKey sender_ratchet_key, uint32 counter, uint32 previous_counter, uint8[] ciphertext, ECPublicKey sender_identity_key, ECPublicKey receiver_identity_key, NativeContext global_context); [CCode (cname = "signal_message_deserialize", cheader_filename = "signal/protocol.h")] public static int signal_message_deserialize(out SignalMessage message, uint8[] data, NativeContext global_context); [CCode (cname = "signal_message_copy", cheader_filename = "signal/protocol.h")] public static int signal_message_copy(out SignalMessage message, SignalMessage other_message, NativeContext global_context); [CCode (cname = "curve_generate_key_pair", cheader_filename = "signal/curve.h")] public static int curve_generate_key_pair(NativeContext context, out ECKeyPair key_pair); [CCode (cname = "curve_decode_private_point", cheader_filename = "signal/curve.h")] public static int curve_decode_private_point(out ECPrivateKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_decode_point", cheader_filename = "signal/curve.h")] public static int curve_decode_point(out ECPublicKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_generate_private_key", cheader_filename = "signal/curve.h")] public static int curve_generate_private_key(NativeContext context, out ECPrivateKey private_key); [CCode (cname = "ratchet_identity_key_pair_deserialize", cheader_filename = "signal/ratchet.h")] public static int ratchet_identity_key_pair_deserialize(out IdentityKeyPair key_pair, uint8[] data, NativeContext global_context); [CCode (cname = "session_signed_pre_key_deserialize", cheader_filename = "signal/signed_pre_key.h")] public static int session_signed_pre_key_deserialize(out SignedPreKeyRecord pre_key, uint8[] data, NativeContext global_context); [Compact] [CCode (cname = "hkdf_context", cprefix = "hkdf_", free_function = "hkdf_destroy", cheader_filename = "signal/hkdf.h")] public class NativeHkdfContext { public static int create(out NativeHkdfContext context, int message_version, NativeContext global_context); public int compare(NativeHkdfContext other); public ssize_t derive_secrets([CCode (array_length = false)] out uint8[] output, uint8[] input_key_material, uint8[] salt, uint8[] info, size_t output_len); } [CCode (cname = "setup_signal_vala_crypto_provider", cheader_filename = "signal_helper.h")] public static void setup_crypto_provider(NativeContext context); [CCode (cname = "signal_vala_randomize", cheader_filename = "signal_helper.h")] public static int native_random(uint8[] data); } dino-0.4.3/plugins/signal-protocol/vapi/signal-protocol-public.vapi0000644000000000000000000004172214452563620024167 0ustar rootrootnamespace Signal { [CCode (cname = "int", cprefix = "SG_ERR_", cheader_filename = "signal/signal_protocol.h", has_type_id = false)] public enum ErrorCode { [CCode (cname = "SG_SUCCESS")] SUCCESS, NOMEM, INVAL, UNKNOWN, DUPLICATE_MESSAGE, INVALID_KEY, INVALID_KEY_ID, INVALID_MAC, INVALID_MESSAGE, INVALID_VERSION, LEGACY_MESSAGE, NO_SESSION, STALE_KEY_EXCHANGE, UNTRUSTED_IDENTITY, VRF_SIG_VERIF_FAILED, INVALID_PROTO_BUF, FP_VERSION_MISMATCH, FP_IDENT_MISMATCH; } [CCode (cname = "SG_ERR_MINIMUM", cheader_filename = "signal/signal_protocol.h")] public const int MIN_ERROR_CODE; [CCode (cname = "int", cprefix = "SG_LOG_", cheader_filename = "signal/signal_protocol.h", has_type_id = false)] public enum LogLevel { ERROR, WARNING, NOTICE, INFO, DEBUG } [CCode (cname = "signal_throw_gerror_by_code_", cheader_filename = "signal/signal_protocol.h")] private int throw_by_code(int code, string? message = null) throws GLib.Error { if (code < 0 && code > MIN_ERROR_CODE) { throw new GLib.Error(-1, code, "%s: %s", message ?? "Signal error", ((ErrorCode)code).to_string()); } return code; } [CCode (cname = "int", cprefix = "SG_CIPHER_", cheader_filename = "signal/signal_protocol.h", has_type_id = false)] public enum Cipher { AES_CTR_NOPADDING, AES_CBC_PKCS5, AES_GCM_NOPADDING } [Compact] [CCode (cname = "signal_type_base", ref_function="signal_type_ref_vapi", unref_function="signal_type_unref_vapi", cheader_filename="signal/signal_protocol_types.h,signal_helper.h")] public class TypeBase { } [Compact] [CCode (cname = "signal_buffer", cheader_filename = "signal/signal_protocol_types.h", free_function="signal_buffer_free")] public class Buffer { [CCode (cname = "signal_buffer_alloc")] public Buffer(size_t len); [CCode (cname = "signal_buffer_create")] public Buffer.from(uint8[] data); public Buffer copy(); public Buffer append(uint8[] data); public int compare(Buffer other); public uint8 get(int i) { return data[i]; } public void set(int i, uint8 val) { data[i] = val; } public uint8[] data { get { int x = (int)len(); unowned uint8[] res = _data(); res.length = x; return res; } } [CCode (array_length = false, cname = "signal_buffer_data")] private unowned uint8[] _data(); private size_t len(); } [Compact] [CCode (cname = "signal_int_list", cheader_filename = "signal/signal_protocol_types.h", free_function="signal_int_list_free")] public class IntList { [CCode (cname = "signal_int_list_alloc")] public IntList(); [CCode (cname = "signal_int_list_push_back")] public int add(int value); public uint size { [CCode (cname = "signal_int_list_size")] get; } [CCode (cname = "signal_int_list_at")] public int get(uint index); } [Compact] [CCode (cname = "session_builder", cprefix = "session_builder_", free_function="session_builder_free", cheader_filename = "signal/session_builder.h")] public class SessionBuilder { [CCode (cname = "session_builder_process_pre_key_bundle")] private int process_pre_key_bundle_(PreKeyBundle pre_key_bundle); [CCode (cname = "session_builder_process_pre_key_bundle_")] public void process_pre_key_bundle(PreKeyBundle pre_key_bundle) throws GLib.Error { throw_by_code(process_pre_key_bundle_(pre_key_bundle)); } } [Compact] [CCode (cname = "session_pre_key_bundle", cprefix = "session_pre_key_bundle_", cheader_filename = "signal/session_pre_key.h")] public class PreKeyBundle : TypeBase { public static int create(out PreKeyBundle bundle, uint32 registration_id, int device_id, uint32 pre_key_id, ECPublicKey? pre_key_public, uint32 signed_pre_key_id, ECPublicKey? signed_pre_key_public, uint8[]? signed_pre_key_signature, ECPublicKey? identity_key); public uint32 registration_id { get; } public int device_id { get; } public uint32 pre_key_id { get; } public ECPublicKey pre_key { owned get; } public uint32 signed_pre_key_id { get; } public ECPublicKey signed_pre_key { owned get; } public Buffer signed_pre_key_signature { owned get; } public ECPublicKey identity_key { owned get; } } [Compact] [CCode (cname = "session_pre_key", cprefix = "session_pre_key_", cheader_filename = "signal/session_pre_key.h,signal_helper.h")] public class PreKeyRecord : TypeBase { public static int create(out PreKeyRecord pre_key, uint32 id, ECKeyPair key_pair); //public static int deserialize(out PreKeyRecord pre_key, uint8[] data, NativeContext global_context); [CCode (instance_pos = 2)] public int serialze(out Buffer buffer); public uint32 id { get; } public ECKeyPair key_pair { get; } } [Compact] [CCode (cname = "session_record", cprefix = "session_record_", cheader_filename = "signal/signal_protocol_types.h")] public class SessionRecord : TypeBase { public SessionState state { get; } public Buffer user_record { get; } } [Compact] [CCode (cname = "session_state", cprefix = "session_state_", cheader_filename = "signal/session_state.h")] public class SessionState : TypeBase { //public static int create(out SessionState state, NativeContext context); //public static int deserialize(out SessionState state, uint8[] data, NativeContext context); //public static int copy(out SessionState state, SessionState other_state, NativeContext context); [CCode (instance_pos = 2)] public int serialze(out Buffer buffer); public uint32 session_version { get; set; } public ECPublicKey local_identity_key { get; set; } public ECPublicKey remote_identity_key { get; set; } //public Ratchet.RootKey root_key { get; set; } public uint32 previous_counter { get; set; } public ECPublicKey sender_ratchet_key { get; } public ECKeyPair sender_ratchet_key_pair { get; } //public Ratchet.ChainKey sender_chain_key { get; set; } public uint32 remote_registration_id { get; set; } public uint32 local_registration_id { get; set; } public int needs_refresh { get; set; } public ECPublicKey alice_base_key { get; set; } } [Compact] [CCode (cname = "session_signed_pre_key", cprefix = "session_signed_pre_key_", cheader_filename = "signal/session_pre_key.h")] public class SignedPreKeyRecord : TypeBase { public static int create(out SignedPreKeyRecord pre_key, uint32 id, uint64 timestamp, ECKeyPair key_pair, uint8[] signature); [CCode (instance_pos = 2)] public int serialze(out Buffer buffer); public uint32 id { get; } public uint64 timestamp { get; } public ECKeyPair key_pair { get; } public uint8[] signature { [CCode (cname = "session_signed_pre_key_get_signature_")] get { int x = (int)get_signature_len(); unowned uint8[] res = get_signature(); res.length = x; return res; } } [CCode (array_length = false, cname = "session_signed_pre_key_get_signature")] private unowned uint8[] get_signature(); private size_t get_signature_len(); } /** * Address of an Signal Protocol message recipient */ [Compact] [CCode (cname = "signal_protocol_address", cprefix = "signal_protocol_address_", cheader_filename = "signal/signal_protocol.h,signal_helper.h")] public class Address { public Address(string name, int32 device_id); public int32 device_id { get; set; } public string name { owned get; set; } } /** * A representation of a (group + sender + device) tuple */ [Compact] [CCode (cname = "signal_protocol_sender_key_name")] public class SenderKeyName { [CCode (cname = "group_id", array_length_cname="group_id_len")] private char* group_id_; private size_t group_id_len; public Address sender; } [Compact] [CCode (cname = "ec_public_key", cprefix = "ec_public_key_", cheader_filename = "signal/curve.h,signal_helper.h")] public class ECPublicKey : TypeBase { [CCode (cname = "curve_generate_public_key")] public static int generate(out ECPublicKey public_key, ECPrivateKey private_key); [CCode (instance_pos = 1, cname = "ec_public_key_serialize")] private int serialize_([CCode (pos = 0)] out Buffer buffer); [CCode (cname = "ec_public_key_serialize_")] public uint8[] serialize() { Buffer buffer; int code = serialize_(out buffer); if (code < 0 && code > MIN_ERROR_CODE) { // Can only throw for invalid arguments or out of memory. GLib.assert_not_reached(); } return buffer.data; } public int compare(ECPublicKey other); public int memcmp(ECPublicKey other); } [Compact] [CCode (cname = "ec_private_key", cprefix = "ec_private_key_", cheader_filename = "signal/curve.h,signal_helper.h")] public class ECPrivateKey : TypeBase { [CCode (instance_pos = 1, cname = "ec_private_key_serialize")] private int serialize_([CCode (pos = 0)] out Buffer buffer); [CCode (cname = "ec_private_key_serialize_")] public uint8[] serialize() throws GLib.Error { Buffer buffer; int code = serialize_(out buffer); if (code < 0 && code > MIN_ERROR_CODE) { // Can only throw for invalid arguments or out of memory. GLib.assert_not_reached(); } return buffer.data; } public int compare(ECPublicKey other); } [Compact] [CCode (cname = "ec_key_pair", cprefix="ec_key_pair_", cheader_filename = "signal/curve.h,signal_helper.h")] public class ECKeyPair : TypeBase { public static int create(out ECKeyPair key_pair, ECPublicKey public_key, ECPrivateKey private_key); public ECPublicKey public { get; } public ECPrivateKey private { get; } } [CCode (cname = "ratchet_message_keys", cheader_filename = "signal/ratchet.h")] public class MessageKeys { } [Compact] [CCode (cname = "ratchet_identity_key_pair", cprefix = "ratchet_identity_key_pair_", cheader_filename = "signal/ratchet.h,signal_helper.h")] public class IdentityKeyPair : TypeBase { public static int create(out IdentityKeyPair key_pair, ECPublicKey public_key, ECPrivateKey private_key); public int serialze(out Buffer buffer); public ECPublicKey public { get; } public ECPrivateKey private { get; } } [Compact] [CCode (cname = "ec_public_key_list")] public class PublicKeyList {} /** * The main entry point for Signal Protocol encrypt/decrypt operations. * * Once a session has been established with session_builder, * this class can be used for all encrypt/decrypt operations within * that session. */ [Compact] [CCode (cname = "session_cipher", cprefix = "session_cipher_", cheader_filename = "signal/session_cipher.h", free_function = "session_cipher_free")] public class SessionCipher { public void* user_data { get; set; } public DecryptionCallback decryption_callback { set; } [CCode (cname = "session_cipher_encrypt")] private int encrypt_(uint8[] padded_message, out CiphertextMessage encrypted_message); [CCode (cname = "session_cipher_encrypt_")] public CiphertextMessage encrypt(uint8[] padded_message) throws GLib.Error { CiphertextMessage res; throw_by_code(encrypt_(padded_message, out res)); return res; } [CCode (cname = "session_cipher_decrypt_pre_key_signal_message")] private int decrypt_pre_key_signal_message_(PreKeySignalMessage ciphertext, void* decrypt_context, out Buffer plaintext); [CCode (cname = "session_cipher_decrypt_pre_key_signal_message_")] public uint8[] decrypt_pre_key_signal_message(PreKeySignalMessage ciphertext, void* decrypt_context = null) throws GLib.Error { Buffer res; throw_by_code(decrypt_pre_key_signal_message_(ciphertext, decrypt_context, out res)); return res.data; } [CCode (cname = "session_cipher_decrypt_signal_message")] private int decrypt_signal_message_(SignalMessage ciphertext, void* decrypt_context, out Buffer plaintext); [CCode (cname = "session_cipher_decrypt_signal_message_")] public uint8[] decrypt_signal_message(SignalMessage ciphertext, void* decrypt_context = null) throws GLib.Error { Buffer res; throw_by_code(decrypt_signal_message_(ciphertext, decrypt_context, out res)); return res.data; } public int get_remote_registration_id(out uint32 remote_id); public int get_session_version(uint32 version); [CCode (has_target = false)] public delegate int DecryptionCallback(SessionCipher cipher, Buffer plaintext, void* decrypt_context); } [CCode (cname = "int", cheader_filename = "signal/protocol.h", has_type_id = false)] public enum CiphertextType { [CCode (cname = "CIPHERTEXT_SIGNAL_TYPE")] SIGNAL, [CCode (cname = "CIPHERTEXT_PREKEY_TYPE")] PREKEY, [CCode (cname = "CIPHERTEXT_SENDERKEY_TYPE")] SENDERKEY, [CCode (cname = "CIPHERTEXT_SENDERKEY_DISTRIBUTION_TYPE")] SENDERKEY_DISTRIBUTION } [Compact] [CCode (cname = "ciphertext_message", cprefix = "ciphertext_message_", cheader_filename = "signal/protocol.h,signal_helper.h")] public abstract class CiphertextMessage : TypeBase { public CiphertextType type { get; } [CCode (cname = "ciphertext_message_get_serialized")] private unowned Buffer get_serialized_(); public uint8[] serialized { [CCode (cname = "ciphertext_message_get_serialized_")] get { return get_serialized_().data; }} } [Compact] [CCode (cname = "signal_message", cprefix = "signal_message_", cheader_filename = "signal/protocol.h,signal_helper.h")] public class SignalMessage : CiphertextMessage { public ECPublicKey sender_ratchet_key { get; } public uint8 message_version { get; } public uint32 counter { get; } public Buffer body { get; } //public int verify_mac(uint8 message_version, ECPublicKey sender_identity_key, ECPublicKey receiver_identity_key, uint8[] mac, NativeContext global_context); public static int is_legacy(uint8[] data); } [Compact] [CCode (cname = "pre_key_signal_message", cprefix = "pre_key_signal_message_", cheader_filename = "signal/protocol.h,signal_helper.h")] public class PreKeySignalMessage : CiphertextMessage { public uint8 message_version { get; } public ECPublicKey identity_key { get; } public uint32 registration_id { get; } public uint32 pre_key_id { get; } public uint32 signed_pre_key_id { get; } public ECPublicKey base_key { get; } public SignalMessage signal_message { get; } } [Compact] [CCode (cname = "sender_key_message", cprefix = "sender_key_message_", cheader_filename = "signal/protocol.h,signal_helper.h")] public class SenderKeyMessage : CiphertextMessage { public uint32 key_id { get; } public uint32 iteration { get; } public Buffer ciphertext { get; } } [Compact] [CCode (cname = "sender_key_distribution_message", cprefix = "sender_key_distribution_message_", cheader_filename = "signal/protocol.h,signal_helper.h")] public class SenderKeyDistributionMessage : CiphertextMessage { public uint32 id { get; } public uint32 iteration { get; } public Buffer chain_key { get; } public ECPublicKey signature_key { get; } } [CCode (cname = "signal_vala_encrypt", cheader_filename = "signal_helper.h")] private static int aes_encrypt_(out Buffer output, int cipher, uint8[] key, uint8[] iv, uint8[] plaintext, void *user_data); [CCode (cname = "signal_vala_encrypt_")] public uint8[] aes_encrypt(int cipher, uint8[] key, uint8[] iv, uint8[] plaintext) throws GLib.Error { Buffer buf; throw_by_code(aes_encrypt_(out buf, cipher, key, iv, plaintext, null)); return buf.data; } [CCode (cname = "signal_vala_decrypt", cheader_filename = "signal_helper.h")] private static int aes_decrypt_(out Buffer output, int cipher, uint8[] key, uint8[] iv, uint8[] ciphertext, void *user_data); [CCode (cname = "signal_vala_decrypt_")] public uint8[] aes_decrypt(int cipher, uint8[] key, uint8[] iv, uint8[] ciphertext) throws GLib.Error { Buffer buf; throw_by_code(aes_decrypt_(out buf, cipher, key, iv, ciphertext, null)); return buf.data; } } dino-0.4.3/qlite/0000755000000000000000000000000014452563620012272 5ustar rootrootdino-0.4.3/qlite/CMakeLists.txt0000644000000000000000000000206514452563620015035 0ustar rootrootfind_packages(QLITE_PACKAGES REQUIRED Gee GLib GObject SQLite3>=3.24 ) vala_precompile(QLITE_VALA_C SOURCES "src/database.vala" "src/table.vala" "src/column.vala" "src/row.vala" "src/statement_builder.vala" "src/delete_builder.vala" "src/insert_builder.vala" "src/query_builder.vala" "src/update_builder.vala" "src/upsert_builder.vala" PACKAGES ${QLITE_PACKAGES} GENERATE_VAPI qlite GENERATE_HEADER qlite ) add_custom_target(qlite-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/qlite.deps ) add_definitions(${VALA_CFLAGS}) add_library(qlite SHARED ${QLITE_VALA_C}) add_dependencies(qlite qlite-vapi) target_link_libraries(qlite ${QLITE_PACKAGES}) set_target_properties(qlite PROPERTIES VERSION 0.1 SOVERSION 0) install(TARGETS qlite ${TARGET_INSTALL}) install(FILES ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/qlite.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/qlite.h DESTINATION ${INCLUDE_INSTALL_DIR}) dino-0.4.3/qlite/src/0000755000000000000000000000000014452563620013061 5ustar rootrootdino-0.4.3/qlite/src/column.vala0000644000000000000000000001413514452563620015227 0ustar rootrootusing Sqlite; namespace Qlite { public abstract class Column { public const string DEFAULT_TABLE_NAME = ""; public string name { get; private set; } public string? default { get; set; } public int sqlite_type { get; private set; } public bool primary_key { get; set; } public bool auto_increment { get; set; } public bool unique { get; set; } public virtual bool not_null { get; set; } public long min_version { get; set; default = -1; } public long max_version { get; set; default = long.MAX; } internal Table table { get; set; } public abstract T get(Row row, string? table_name = DEFAULT_TABLE_NAME); public virtual bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return false; } internal abstract void bind(Statement stmt, int index, T value); public string to_string() { return table == null ? name : (table.name + "." + name); } public string to_column_definition() { string res = name; switch (sqlite_type) { case INTEGER: res += " INTEGER"; break; case FLOAT: res += " REAL"; break; case TEXT: res += " TEXT"; break; default: res += " UNKNOWN"; break; } if (primary_key) { res += " PRIMARY KEY"; if (auto_increment) res += " AUTOINCREMENT"; } if (not_null) res += " NOT NULL"; if (unique) res += " UNIQUE"; if (default != null) res += @" DEFAULT $((!) default)"; return res; } Column(string name, int type) { this.name = name; this.sqlite_type = type; } public class Integer : Column { public Integer(string name) { base(name, INTEGER); } public override int get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (int) row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, int value) { stmt.bind_int(index, value); } } public class Long : Column { public Long(string name) { base(name, INTEGER); } public override long get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (long) row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, long value) { stmt.bind_int64(index, value); } } public class NullableReal : Column { public NullableReal(string name) { base(name, FLOAT); } public override bool not_null { get { return false; } set {} } public override double? get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_real(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_real(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, double? value) { stmt.bind_double(index, value); } } public class Text : Column { public Text(string name) { base(name, TEXT); } public override string? get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return get(row, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == null; } internal override void bind(Statement stmt, int index, string? value) { if (value != null) { stmt.bind_text(index, (!) value); } else { stmt.bind_null(index); } } } public class NonNullText : Column { public NonNullText(string name) { base(name, TEXT); } public override bool not_null { get { return true; } set {} } public override string get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (!)row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return false; } internal override void bind(Statement stmt, int index, string value) { stmt.bind_text(index, (!) value); } } public class BoolText : Column { public BoolText(string name) { base(name, TEXT); } public override bool get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == "1"; } internal override void bind(Statement stmt, int index, bool value) { stmt.bind_text(index, value ? "1" : "0"); } } public class BoolInt : Column { public BoolInt(string name) { base(name, INTEGER); } public override bool get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == 1; } internal override void bind(Statement stmt, int index, bool value) { stmt.bind_int(index, value ? 1 : 0); } } } } dino-0.4.3/qlite/src/database.vala0000644000000000000000000001204614452563620015475 0ustar rootrootusing Sqlite; namespace Qlite { public class Database { private string file_name; private Sqlite.Database db; private long expected_version; private Table[]? tables; private Column meta_name = new Column.Text("name") { primary_key = true }; private Column meta_int_val = new Column.Long("int_val"); private Column meta_text_val = new Column.Text("text_val"); private Table meta_table; public bool debug = false; public Database(string file_name, long expected_version) { this.file_name = file_name; this.expected_version = expected_version; meta_table = new Table(this, "_meta"); meta_table.init({meta_name, meta_int_val, meta_text_val}); } public void init(Table[] tables) { Sqlite.config(Config.SERIALIZED); int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000); if (ec != Sqlite.OK) { error(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } this.tables = tables; if (debug) db.trace((message) => print(@"Qlite trace: $message\n")); start_migration(); } public void ensure_init() { if (tables == null) error(@"Database $file_name was not initialized, call init()"); } private void start_migration() { try { exec("BEGIN TRANSACTION"); } catch (Error e) { error("SQLite error: %d - %s", db.errcode(), db.errmsg()); } meta_table.create_table_at_version(expected_version); long old_version = 0; old_version = meta_table.row_with(meta_name, "version")[meta_int_val, -1]; if (old_version == -1) { foreach (Table t in tables) { t.create_table_at_version(expected_version); } meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform(); } else if (expected_version != old_version) { foreach (Table t in tables) { t.create_table_at_version(old_version); } foreach (Table t in tables) { t.add_columns_for_version(old_version, expected_version); } migrate(old_version); foreach (Table t in tables) { t.delete_columns_for_version(old_version, expected_version); } if (old_version == -1) { meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform(); } else { meta_table.update().with(meta_name, "=", "version").set(meta_int_val, expected_version).perform(); } } foreach (Table t in tables) { t.post(); } try { exec("END TRANSACTION"); } catch (Error e) { error("SQLite error: %d - %s", db.errcode(), db.errmsg()); } } internal int errcode() { return db.errcode(); } internal string errmsg() { return db.errmsg(); } internal int64 last_insert_rowid() { return db.last_insert_rowid(); } // To be implemented by actual implementation if required // new table columns are added, outdated columns are still present and will be removed afterwards public virtual void migrate(long old_version) { } public QueryBuilder select(Column[]? columns = null) { ensure_init(); return new QueryBuilder(this).select(columns); } internal MatchQueryBuilder match_query(Table table) { ensure_init(); return new MatchQueryBuilder(this, table); } public InsertBuilder insert() { ensure_init(); return new InsertBuilder(this); } public UpdateBuilder update(Table table) { ensure_init(); return new UpdateBuilder(this, table); } public UpsertBuilder upsert(Table table) { ensure_init(); return new UpsertBuilder(this, table); } public UpdateBuilder update_named(string table) { ensure_init(); return new UpdateBuilder.for_name(this, table); } public DeleteBuilder delete() { ensure_init(); return new DeleteBuilder(this); } public RowIterator query_sql(string sql, string[]? args = null) { ensure_init(); return new RowIterator(this, sql, args); } internal Statement prepare(string sql) { ensure_init(); Sqlite.Statement statement; if (db.prepare_v2(sql, sql.length, out statement) != OK) { error("SQLite error: %d - %s: %s", db.errcode(), db.errmsg(), sql); } return statement; } public void exec(string sql) throws Error { ensure_init(); if (db.exec(sql) != OK) { throw new Error(-1, 0, "SQLite error: %d - %s", db.errcode(), db.errmsg()); } } public bool is_known_column(string table, string field) { ensure_init(); foreach (Table t in tables) { if (t.is_known_column(field)) return true; } return false; } } } dino-0.4.3/qlite/src/delete_builder.vala0000644000000000000000000000332014452563620016674 0ustar rootrootusing Sqlite; namespace Qlite { public class DeleteBuilder : StatementBuilder { // DELETE FROM [...] private Table? table; private string table_name; // WHERE [...] private string selection = "1"; private StatementBuilder.AbstractField[] selection_args = {}; internal DeleteBuilder(Database db) { base(db); } public DeleteBuilder from(Table table) { if (this.table != null) error("Qlite Error: ILLEGAL QUERY: cannot use from() multiple times."); this.table = table; this.table_name = table.name; return this; } public DeleteBuilder from_name(string table) { this.table_name = table; return this; } public DeleteBuilder where(string selection, string[]? selection_args = null) { if (this.selection != "1") error("selection was already done, but where() was called."); this.selection = selection; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public DeleteBuilder with(Column column, string comp, T value) { selection_args += new Field(column, value); selection = @"($selection) AND $(column.name) $comp ?"; return this; } internal override Statement prepare() { Statement stmt = db.prepare(@"DELETE FROM $table_name WHERE $selection"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } return stmt; } public void perform() { if (prepare().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } } } } dino-0.4.3/qlite/src/insert_builder.vala0000644000000000000000000000415214452563620016742 0ustar rootrootusing Sqlite; namespace Qlite { public class InsertBuilder : StatementBuilder { // INSERT [OR ...] private bool replace_val; private string? or_val; // INTO [...] private Table table; private string table_name; // VALUES [...] private StatementBuilder.AbstractField[] fields = {}; internal InsertBuilder(Database db) { base(db); } public InsertBuilder replace() { this.replace_val = true; return this; } public InsertBuilder or(string or) { this.or_val = or; return this; } public InsertBuilder into(Table table) { this.table = table; this.table_name = table.name; return this; } public InsertBuilder into_name(string table) { this.table_name = table; return this; } public InsertBuilder value(Column column, T value) { fields += new Field(column, value); return this; } public InsertBuilder value_null(Column column) { if (column.not_null) error("Qlite Error: ILLEGAL QUERY: Can't set non-null column %s to null", column.name); fields += new NullField(column); return this; } internal override Statement prepare() { string fields_text = ""; string value_qs = ""; for (int i = 0; i < fields.length; i++) { if (i != 0) { value_qs += ", "; fields_text += ", "; } fields_text += ((!)fields[i].column).name; value_qs += "?"; } string sql = replace_val ? "REPLACE" : "INSERT"; if (!replace_val && or_val != null) sql += @" OR $((!)or_val)"; sql += @" INTO $table_name ( $fields_text ) VALUES ($value_qs)"; Statement stmt = db.prepare(sql); for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i+1); } return stmt; } public int64 perform() { if (prepare().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } return db.last_insert_rowid(); } } } dino-0.4.3/qlite/src/query_builder.vala0000644000000000000000000001632714452563620016612 0ustar rootrootusing Sqlite; namespace Qlite { public class QueryBuilder : StatementBuilder { private bool single_result; // SELECT [...] private string column_selector = "*"; private Column[] columns = {}; // FROM [...] protected Table? table; protected string? table_name; // JOIN [...] private string joins = ""; // WHERE [...] protected string selection = "1"; internal StatementBuilder.AbstractField[] selection_args = {}; // ORDER BY [...] private OrderingTerm[]? order_by_terms = {}; // GROUP BY [...] private string? group_by_term; // LIMIT [...] OFFSET [...] private int limit_val; private int offset_val; internal QueryBuilder(Database db) { base(db); } public QueryBuilder select(Column[] columns = {}) { this.columns = columns; if (columns.length != 0) { for (int i = 0; i < columns.length; i++) { if (column_selector == "*") { column_selector = columns[i].to_string(); } else { column_selector += ", " + columns[i].to_string(); } } } else { column_selector = "*"; } return this; } public QueryBuilder select_string(string column_selector) { this.columns = {}; this.column_selector = column_selector; return this; } public virtual QueryBuilder from(Table table) { if (this.table_name != null) error("cannot use from() multiple times."); this.table = table; this.table_name = table.name; return this; } public virtual QueryBuilder from_name(string table) { this.table_name = table; return this; } public QueryBuilder outer_join_with(Table table, Column lhs, Column rhs, string? as = null) { return outer_join_on(table, @"$lhs = $rhs", as); } public QueryBuilder outer_join_on(Table table, string on, string? as = null) { if (as == null) as = table.name; joins += @" LEFT OUTER JOIN $(table.name) AS $as ON $on"; return this; } public QueryBuilder join_with(Table table, Column lhs, Column rhs, string? as = null) { return join_on(table, @"$lhs = $rhs", as); } public QueryBuilder join_on(Table table, string on, string? as = null) { if (as == null) as = table.name; joins += @" JOIN $(table.name) AS $as ON $on"; return this; } internal QueryBuilder join_name(string table_name, string on) { joins += @" JOIN $table_name ON $on"; return this; } public QueryBuilder where(string selection, string[] selection_args = {}) { this.selection = @"($(this.selection)) AND ($selection)"; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public QueryBuilder with(Column column, string comp, T value) { if ((column.unique || column.primary_key) && comp == "=") single_result = true; selection_args += new Field(column, value); selection = @"($selection) AND $column $comp ?"; return this; } public QueryBuilder with_null(Column column) { selection = @"($selection) AND $column ISNULL"; return this; } public QueryBuilder without_null(Column column) { selection = @"($selection) AND $column NOT NULL"; return this; } public QueryBuilder order_by(Column column, string dir = "ASC") { order_by_terms += new OrderingTerm(column, dir); return this; } public QueryBuilder order_by_name(string name, string dir) { order_by_terms += new OrderingTerm.by_name(name, dir); return this; } public QueryBuilder group_by(Column[] columns) { foreach(Column col in columns) { if (group_by_term == null) { group_by_term = col.to_string(); } else { group_by_term += @", $col"; } } return this; } public QueryBuilder limit(int limit) { if (this.limit_val != 0 && limit > this.limit_val) error("tried to increase an existing limit"); this.limit_val = limit; return this; } public QueryBuilder offset(int offset) { if (this.limit_val == 0) error("limit required before offset"); this.offset_val = offset; return this; } public QueryBuilder single() { this.single_result = true; return limit(1); } public int64 count() { this.column_selector = @"COUNT($column_selector) AS count"; this.single_result = true; return row().get_integer("count"); } private Row? row_() { if (!single_result) error("query is not suited to return a single row, but row() was called."); return iterator().get_next(); } public RowOption row() { return new RowOption(row_()); } public T get(Column field, T def = null) { return row().get(field, def); } internal override Statement prepare() { Statement stmt = db.prepare(@"SELECT $column_selector $(table_name == null ? "" : @"FROM $((!) table_name)") $joins WHERE $selection $(group_by_term == null ? "" : @"GROUP BY $group_by_term") $(OrderingTerm.all_to_string(order_by_terms)) $(limit_val > 0 ? @" LIMIT $limit_val OFFSET $offset_val" : "")"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } return stmt; } public RowIterator iterator() { return new RowIterator.from_query_builder(db, this); } class OrderingTerm { Column? column; string column_name; string dir; public OrderingTerm(Column column, string dir) { this.column = column; this.column_name = column.to_string(); this.dir = dir; } public OrderingTerm.by_name(string column_name, string dir) { this.column_name = column_name; this.dir = dir; } public string to_string() { return @"$column_name $dir"; } public static string all_to_string(OrderingTerm[]? terms) { if (terms == null || terms.length == 0) return ""; string res = "ORDER BY "+terms[0].to_string(); for (int i = 1; i < terms.length; i++) { res += @", $(terms[i])"; } return res; } } } public class MatchQueryBuilder : QueryBuilder { internal MatchQueryBuilder(Database db, Table table) { base(db); if (table.fts_columns == null) error("MATCH query on non FTS table"); from(table); join_name(@"_fts_$table_name", @"_fts_$table_name.docid = $table_name.rowid"); } public MatchQueryBuilder match(Column column, string match) { if (table == null) error("MATCH must occur after FROM statement"); if (!(column in table.fts_columns)) error("MATCH selection on non FTS column"); selection_args += new StatementBuilder.StringField(match); selection = @"($selection) AND _fts_$table_name.$(column.name) MATCH ?"; return this; } } } dino-0.4.3/qlite/src/row.vala0000644000000000000000000001007014452563620014533 0ustar rootrootusing Gee; using Sqlite; namespace Qlite { public class Row { private Map text_map = new HashMap(); private Map int_map = new HashMap(); private Map real_map = new HashMap(); internal Row(Statement stmt) { for (int i = 0; i < stmt.column_count(); i++) { string column_name; if (stmt.column_origin_name(i) != null) { column_name = @"$(stmt.column_table_name(i)).$(stmt.column_origin_name(i))"; } else { column_name = stmt.column_name(i); } switch(stmt.column_type(i)) { case TEXT: text_map[column_name] = stmt.column_text(i); break; case INTEGER: int_map[column_name] = (long) stmt.column_int64(i); break; case FLOAT: real_map[column_name] = stmt.column_double(i); break; } } } public T get(Column field) { return field[this]; } private string field_name(string field, string? table) { if (table != null) { return @"$table.$field"; } else { return field; } } public string? get_text(string field, string? table = null) { if (text_map.has_key(field_name(field, table))) { return text_map[field_name(field, table)]; } return null; } public long get_integer(string field, string? table = null) { return int_map[field_name(field, table)]; } public bool has_integer(string field, string? table = null) { return int_map.has_key(field_name(field, table)); } public double get_real(string field, string? table = null, double def = 0) { return real_map[field_name(field, table)] ?? def; } public bool has_real(string field, string? table = null) { return real_map.has_key(field_name(field, table)) && real_map[field_name(field, table)] != null; } public string to_string() { string ret = "{"; foreach (string key in text_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: \"$(text_map[key])\""; } foreach (string key in int_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: $(int_map[key])"; } foreach (string key in real_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: $(real_map[key])"; } return ret + "}"; } } public class RowIterator { private Database db; private Statement stmt; public RowIterator.from_query_builder(Database db, QueryBuilder query) { this.db = db; this.stmt = query.prepare(); } public RowIterator(Database db, string sql, string[]? args = null) { this.db = db; this.stmt = db.prepare(sql); if (args != null) { for (int i = 0; i < args.length; i++) { stmt.bind_text(i, sql, sql.length); } } } public bool next() { int r = stmt.step(); if (r == Sqlite.ROW) return true; if (r == Sqlite.DONE) return false; warning(@"SQLite error: $(db.errcode()) - $(db.errmsg())"); return false; } public Row get() { return new Row(stmt); } public Row? get_next() { if (next()) return get(); return null; } } public class RowOption { public Row? inner { get; private set; } public RowOption(Row? row) { this.inner = row; } public bool is_present() { return inner != null; } public T get(Column field, T def = null) { if (inner == null || field.is_null((!)inner)) return def; return field[(!)inner]; } internal long get_integer(string field, long def = 0) { if (inner == null || !((!)inner).has_integer(field)) return def; return ((!)inner).get_integer(field); } } } dino-0.4.3/qlite/src/statement_builder.vala0000644000000000000000000000245314452563620017444 0ustar rootrootusing Sqlite; namespace Qlite { public abstract class StatementBuilder { protected Database db; internal StatementBuilder(Database db) { this.db = db; } internal abstract Statement prepare(); internal abstract class AbstractField { public T value; public Column? column; AbstractField(T value) { this.value = value; } internal abstract void bind(Statement stmt, int index); } internal class Field : AbstractField { public Field(Column column, T value) { base(value); this.column = column; } internal override void bind(Statement stmt, int index) { ((!)column).bind(stmt, index, value); } } internal class NullField : AbstractField { public NullField(Column column) { base(null); this.column = column; } internal override void bind(Statement stmt, int index) { stmt.bind_null(index); } } internal class StringField : AbstractField { public StringField(string value) { base(value); } internal override void bind(Statement stmt, int index) { stmt.bind_text(index, value); } } } } dino-0.4.3/qlite/src/table.vala0000644000000000000000000001646414452563620015030 0ustar rootrootusing Sqlite; namespace Qlite { public class Table { protected Database db; public string name { get; private set; } protected Column[]? columns; private string constraints = ""; private string[] post_statements = {}; private string[] create_statements = {}; internal Column[]? fts_columns; public Table(Database db, string name) { this.db = db; this.name = name; } public void init(Column[] columns, string constraints = "") { this.columns = columns; this.constraints = constraints; foreach(Column c in columns) { c.table = this; } } public void fts(Column[] columns) { if (fts_columns != null) error("Only one FTS index may be used per table."); fts_columns = columns; string cs = ""; string cnames = ""; string cnews = ""; foreach (Column c in columns) { cs += @", $(c.to_column_definition())"; cnames += @", $(c.name)"; cnews += @", new.$(c.name)"; } add_create_statement(@"CREATE VIRTUAL TABLE IF NOT EXISTS _fts_$name USING fts4(tokenize=unicode61, content=\"$name\"$cs)"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bu_$(name) BEFORE UPDATE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bd_$(name) BEFORE DELETE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_au_$(name) AFTER UPDATE ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_ai_$(name) AFTER INSERT ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); } public void fts_rebuild() { if (fts_columns == null) error("FTS not available on this table."); try { db.exec(@"INSERT INTO _fts_$name(_fts_$name) VALUES('rebuild');"); } catch (Error e) { critical(@"Qlite Error: Rebuilding FTS index: $(e.message)"); } } public void unique(Column[] columns, string? on_conflict = null) { constraints += ", UNIQUE ("; bool first = true; foreach (Column c in columns) { if (!first) constraints += ", "; constraints += c.name; first = false; } constraints += ")"; if (on_conflict != null) { constraints += " ON CONFLICT " + (!)on_conflict; } } public void add_post_statement(string stmt) { post_statements += stmt; } public void add_create_statement(string stmt) { create_statements += stmt; } public void index(string index_name, Column[] columns, bool unique = false) { string stmt = @"CREATE $(unique ? "UNIQUE" : "") INDEX IF NOT EXISTS $index_name ON $name ("; bool first = true; foreach (Column c in columns) { if (!first) stmt += ", "; stmt += c.name; first = false; } stmt += ")"; add_post_statement(stmt); } private void ensure_init() { if (columns == null) error("Table %s was not initialized, call init()", name); } public QueryBuilder select(Column[]? columns = null) { ensure_init(); return db.select(columns).from(this); } private MatchQueryBuilder match_query() { ensure_init(); return db.match_query(this); } public MatchQueryBuilder match(Column column, string query) { return match_query().match(column, query); } public InsertBuilder insert() { ensure_init(); return db.insert().into(this); } public UpdateBuilder update() { ensure_init(); return db.update(this); } public UpsertBuilder upsert() { ensure_init(); return db.upsert(this); } public DeleteBuilder delete() { ensure_init(); return db.delete().from(this); } public RowOption row_with(Column column, T value) { ensure_init(); if (!column.unique && !column.primary_key) error("%s is not suited to identify a row, but used with row_with()", column.name); return select().with(column, "=", value).row(); } public bool is_known_column(string column) { ensure_init(); foreach (Column c in columns) { if (c.name == column) return true; } return false; } public void create_table_at_version(long version) { ensure_init(); string sql = @"CREATE TABLE IF NOT EXISTS $name ("; bool first = true; for (int i = 0; i < columns.length; i++) { Column c = columns[i]; if (c.min_version <= version && c.max_version >= version) { sql += @"$(!first ? "," : "") $(c.to_column_definition())"; first = false; } } sql += @"$constraints)"; try { db.exec(sql); } catch (Error e) { error(@"Qlite Error: Create table at version: $(e.message)"); } foreach (string stmt in create_statements) { try { db.exec(stmt); } catch (Error e) { error(@"Qlite Error: Create table at version: $(e.message)"); } } } public void add_columns_for_version(long old_version, long new_version) { ensure_init(); foreach (Column c in columns) { if (c.min_version <= new_version && c.max_version >= new_version && c.min_version > old_version) { try { db.exec(@"ALTER TABLE $name ADD COLUMN $(c.to_column_definition())"); } catch (Error e) { critical(@"Qlite Error: Add columns for version: $(e.message)"); } } } } public void delete_columns_for_version(long old_version, long new_version) { bool column_deletion_required = false; string column_list = ""; foreach (Column c in columns) { if (c.min_version <= new_version && c.max_version >= new_version) { if (column_list == "") { column_list = c.name; } else { column_list += ", " + c.name; } } if (!(c.min_version <= new_version && c.max_version >= new_version) && c.min_version <= old_version && c.max_version >= old_version) { column_deletion_required = true; } } if (column_deletion_required) { try { db.exec(@"ALTER TABLE $name RENAME TO _$(name)_$old_version"); create_table_at_version(new_version); db.exec(@"INSERT INTO $name ($column_list) SELECT $column_list FROM _$(name)_$old_version"); db.exec(@"DROP TABLE _$(name)_$old_version"); } catch (Error e) { error(@"Qlite Error: Delete columns for version change: $(e.message)"); } } } internal void post() { foreach (string stmt in post_statements) { try { db.exec(stmt); } catch (Error e) { error(@"Qlite Error: Post: $(e.message)"); } } } } } dino-0.4.3/qlite/src/update_builder.vala0000644000000000000000000000554714452563620016731 0ustar rootrootusing Sqlite; namespace Qlite { public class UpdateBuilder : StatementBuilder { // UPDATE [OR ...] private string? or_val; // [...] private Table? table; private string table_name; // SET [...] private StatementBuilder.AbstractField[] fields = {}; // WHERE [...] private string selection = "1"; private StatementBuilder.AbstractField[] selection_args = {}; internal UpdateBuilder(Database db, Table table) { base(db); this.table = table; this.table_name = table.name; } internal UpdateBuilder.for_name(Database db, string table) { base(db); this.table_name = table; } public UpdateBuilder or(string or) { this.or_val = or; return this; } public UpdateBuilder set(Column column, T value) { fields += new Field(column, value); return this; } public UpdateBuilder set_null(Column column) { if (column.not_null) error("Can't set non-null column %s to null", column.name); fields += new NullField(column); return this; } public UpdateBuilder where(string selection, string[] selection_args = {}) { if (this.selection != "1") error("selection was already done, but where() was called."); this.selection = selection; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public UpdateBuilder with(Column column, string comp, T value) { selection_args += new Field(column, value); selection = @"($selection) AND $(column.name) $comp ?"; return this; } public UpdateBuilder with_null(Column column) { selection = @"($selection) AND $(column.name) ISNULL"; return this; } public UpdateBuilder without_null(Column column) { selection = @"($selection) AND $(column.name) NOT NULL"; return this; } internal override Statement prepare() { string sql = "UPDATE"; if (or_val != null) sql += @" OR $((!)or_val)"; sql += @" $table_name SET "; for (int i = 0; i < fields.length; i++) { if (i != 0) { sql += ", "; } sql += @"$(((!)fields[i].column).name) = ?"; } sql += @" WHERE $selection"; Statement stmt = db.prepare(sql); for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i+1); } for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i + fields.length + 1); } return stmt; } public void perform() { if (fields.length == 0) return; if (prepare().step() != DONE) { critical("SQLite error: %d - %s", db.errcode(), db.errmsg()); } } } } dino-0.4.3/qlite/src/upsert_builder.vala0000644000000000000000000000533214452563620016761 0ustar rootrootusing Sqlite; namespace Qlite { public class UpsertBuilder : StatementBuilder { // INTO [...] private Table table; private string table_name; // VALUES [...] private StatementBuilder.AbstractField[] keys = {}; private StatementBuilder.AbstractField[] fields = {}; internal UpsertBuilder(Database db, Table table) { base(db); this.table = table; this.table_name = table.name; } public UpsertBuilder value(Column column, T value, bool key = false) { if (key) { keys += new Field(column, value); } else { fields += new Field(column, value); } return this; } public UpsertBuilder value_null(Column column) { if (column.not_null) error("Can't set non-null column %s to null", column.name); fields += new NullField(column); return this; } internal override Statement prepare() { error("prepare() not available for upsert."); } internal Statement prepare_upsert() { var unique_fields = new StringBuilder(); var unique_values = new StringBuilder(); var update_fields = new StringBuilder(); var update_values = new StringBuilder(); var update_fields_vals = new StringBuilder(); for (int i = 0; i < keys.length; i++) { if (i != 0) { unique_fields.append(", "); unique_values.append(", "); } unique_fields.append(keys[i].column.name); unique_values.append("?"); } for (int i = 0; i < fields.length; i++) { if (i != 0) { update_fields.append(", "); update_values.append(", "); update_fields_vals.append(", "); } update_fields.append(fields[i].column.name); update_values.append("?"); update_fields_vals.append(fields[i].column.name).append("=excluded.").append(fields[i].column.name); } string sql = @"INSERT INTO $table_name ($(unique_fields.str), $(update_fields.str)) VALUES ($(unique_values.str), $(update_values.str)) " + @"ON CONFLICT ($(unique_fields.str)) DO UPDATE SET $(update_fields_vals.str)"; Statement stmt = db.prepare(sql); for (int i = 0; i < keys.length; i++) { keys[i].bind(stmt, i + 1); } for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i + keys.length + 1); } return stmt; } public int64 perform() { if (prepare_upsert().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } return db.last_insert_rowid(); } } } dino-0.4.3/xmpp-vala/0000755000000000000000000000000014452563620013061 5ustar rootrootdino-0.4.3/xmpp-vala/CMakeLists.txt0000644000000000000000000001543614452563620015632 0ustar rootrootfind_packages(ENGINE_PACKAGES REQUIRED GDKPixbuf2 Gee GIO GLib GObject ICU ) set(ENGINE_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) vala_precompile(ENGINE_VALA_C SOURCES "src/core/direct_tls_xmpp_stream.vala" "src/core/io_xmpp_stream.vala" "src/core/module_flag.vala" "src/core/starttls_xmpp_stream.vala" "src/core/stream_connect.vala" "src/core/tls_xmpp_stream.vala" "src/core/xmpp_stream.vala" "src/core/namespace_state.vala" "src/core/stanza_attribute.vala" "src/core/stanza_node.vala" "src/core/stanza_reader.vala" "src/core/stanza_writer.vala" "src/core/xmpp_log.vala" "src/module/bind.vala" "src/module/bookmarks_provider.vala" "src/module/conference.vala" "src/module/iq/module.vala" "src/module/iq/stanza.vala" "src/module/jid.vala" "src/module/message/module.vala" "src/module/message/stanza.vala" "src/module/presence/flag.vala" "src/module/presence/module.vala" "src/module/presence/stanza.vala" "src/module/roster/flag.vala" "src/module/roster/item.vala" "src/module/roster/module.vala" "src/module/roster/versioning_module.vala" "src/module/sasl.vala" "src/module/session.vala" "src/module/stanza.vala" "src/module/stanza_error.vala" "src/module/stream_error.vala" "src/module/util.vala" "src/module/xep/0048_bookmarks.vala" "src/module/xep/0048_conference.vala" "src/module/xep/0402_bookmarks2.vala" "src/module/xep/0004_data_forms.vala" "src/module/xep/0030_service_discovery/flag.vala" "src/module/xep/0030_service_discovery/identity.vala" "src/module/xep/0030_service_discovery/info_result.vala" "src/module/xep/0030_service_discovery/item.vala" "src/module/xep/0030_service_discovery/items_result.vala" "src/module/xep/0030_service_discovery/module.vala" "src/module/xep/0045_muc/flag.vala" "src/module/xep/0045_muc/module.vala" "src/module/xep/0045_muc/status_code.vala" "src/module/xep/0047_in_band_bytestreams.vala" "src/module/xep/0049_private_xml_storage.vala" "src/module/xep/0059_result_set_management.vala" "src/module/xep/0054_vcard/module.vala" "src/module/xep/0060_pubsub.vala" "src/module/xep/0065_socks5_bytestreams.vala" "src/module/xep/0066_out_of_band_data.vala" "src/module/xep/0077_in_band_registration.vala" "src/module/xep/0082_date_time_profiles.vala" "src/module/xep/0084_user_avatars.vala" "src/module/xep/0085_chat_state_notifications.vala" "src/module/xep/0115_entity_capabilities.vala" "src/module/xep/0166_jingle/content.vala" "src/module/xep/0166_jingle/content_description.vala" "src/module/xep/0166_jingle/content_node.vala" "src/module/xep/0166_jingle/content_security.vala" "src/module/xep/0166_jingle/content_transport.vala" "src/module/xep/0166_jingle/component.vala" "src/module/xep/0166_jingle/jingle_flag.vala" "src/module/xep/0166_jingle/jingle_module.vala" "src/module/xep/0166_jingle/jingle_structs.vala" "src/module/xep/0166_jingle/reason_element.vala" "src/module/xep/0166_jingle/session.vala" "src/module/xep/0166_jingle/session_info.vala" "src/module/xep/0167_jingle_rtp/content_parameters.vala" "src/module/xep/0167_jingle_rtp/content_type.vala" "src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala" "src/module/xep/0167_jingle_rtp/payload_type.vala" "src/module/xep/0167_jingle_rtp/session_info_type.vala" "src/module/xep/0167_jingle_rtp/stream.vala" "src/module/xep/0176_jingle_ice_udp/candidate.vala" "src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala" "src/module/xep/0176_jingle_ice_udp/transport_parameters.vala" "src/module/xep/0177_jingle_raw_udp.vala" "src/module/xep/0384_omemo/omemo_encryptor.vala" "src/module/xep/0384_omemo/omemo_decryptor.vala" "src/module/xep/0184_message_delivery_receipts.vala" "src/module/xep/0191_blocking_command.vala" "src/module/xep/0198_stream_management.vala" "src/module/xep/0199_ping.vala" "src/module/xep/0203_delayed_delivery.vala" "src/module/xep/0215_external_service_discovery.vala" "src/module/xep/0234_jingle_file_transfer.vala" "src/module/xep/0249_direct_muc_invitations.vala" "src/module/xep/0260_jingle_socks5_bytestreams.vala" "src/module/xep/0261_jingle_in_band_bytestreams.vala" "src/module/xep/0272_muji.vala" "src/module/xep/0280_message_carbons.vala" "src/module/xep/0297_stanza_forwarding.vala" "src/module/xep/0298_coin.vala" "src/module/xep/0308_last_message_correction.vala" "src/module/xep/0313_message_archive_management.vala" "src/module/xep/0313_2_message_archive_management.vala" "src/module/xep/0333_chat_markers.vala" "src/module/xep/0334_message_processing_hints.vala" "src/module/xep/0353_jingle_message_initiation.vala" "src/module/xep/0353_call_invite_message.vala" "src/module/xep/0359_unique_stable_stanza_ids.vala" "src/module/xep/0363_http_file_upload.vala" "src/module/xep/0380_explicit_encryption.vala" "src/module/xep/0391_jingle_encrypted_transports.vala" "src/module/xep/0410_muc_self_ping.vala" "src/module/xep/0421_occupant_ids.vala" "src/module/xep/0428_fallback_indication.vala" "src/module/xep/0444_reactions.vala" "src/module/xep/0461_replies.vala" "src/module/xep/pixbuf_storage.vala" "src/util.vala" PACKAGES ${ENGINE_PACKAGES} GENERATE_VAPI xmpp-vala GENERATE_HEADER xmpp-vala CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/src/glib_fixes.vapi" OPTIONS ${ENGINE_EXTRA_OPTIONS} ) add_custom_target(xmpp-vala-vapi DEPENDS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.deps ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="xmpp-vala") add_library(xmpp-vala SHARED ${ENGINE_VALA_C}) add_dependencies(xmpp-vala xmpp-vala-vapi) target_link_libraries(xmpp-vala ${ENGINE_PACKAGES}) set_target_properties(xmpp-vala PROPERTIES VERSION 0.1 SOVERSION 0) install(TARGETS xmpp-vala ${TARGET_INSTALL}) install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.h DESTINATION ${INCLUDE_INSTALL_DIR}) if(BUILD_TESTS) vala_precompile(ENGINE_TEST_VALA_C SOURCES "tests/common.vala" "tests/testcase.vala" "tests/jid.vala" "tests/stanza.vala" "tests/util.vala" CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala_internal.vapi PACKAGES ${ENGINE_PACKAGES} OPTIONS ${ENGINE_EXTRA_OPTIONS} ) add_definitions(${VALA_CFLAGS}) add_executable(xmpp-vala-test ${ENGINE_TEST_VALA_C}) target_link_libraries(xmpp-vala-test xmpp-vala ${SIGNAL_PROTOCOL_PACKAGES}) endif(BUILD_TESTS) dino-0.4.3/xmpp-vala/src/0000755000000000000000000000000014452563620013650 5ustar rootrootdino-0.4.3/xmpp-vala/src/core/0000755000000000000000000000000014452563620014600 5ustar rootrootdino-0.4.3/xmpp-vala/src/core/direct_tls_xmpp_stream.vala0000644000000000000000000000270214452563620022221 0ustar rootrootpublic class Xmpp.DirectTlsXmppStream : TlsXmppStream { const string[] ADVERTISED_PROTOCOLS = {"xmpp-client", null}; string host; uint16 port; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert; public DirectTlsXmppStream(Jid remote_name, string host, uint16 port, TlsXmppStream.OnInvalidCertWrapper on_invalid_cert) { base(remote_name); this.host = host; this.port = port; this.on_invalid_cert = on_invalid_cert; } public override async void connect() throws IOError { SocketClient client = new SocketClient(); try { debug("Connecting to %s:%i (tls)", host, port); IOStream? io_stream = yield client.connect_to_host_async(host, port); TlsConnection tls_connection = TlsClientConnection.new(io_stream, new NetworkAddress(remote_name.to_string(), port)); #if GLIB_2_60 tls_connection.set_advertised_protocols(ADVERTISED_PROTOCOLS); #endif tls_connection.accept_certificate.connect(on_invalid_certificate); tls_connection.accept_certificate.connect((cert, flags) => on_invalid_cert.func(cert, flags)); reset_stream(tls_connection); yield setup(); attach_negotation_modules(); } catch (IOError e) { throw e; } catch (Error e) { throw new IOError.CONNECTION_REFUSED("Failed connecting to %s:%i (tls): %s", host, port, e.message); } } } dino-0.4.3/xmpp-vala/src/core/io_xmpp_stream.vala0000644000000000000000000000656314452563620020505 0ustar rootrootusing Gee; public interface Xmpp.WriteNodeFunc : Object { public abstract async void write_stanza(XmppStream stream, StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError; } public abstract class Xmpp.IoXmppStream : XmppStream { private IOStream? stream; internal StanzaReader? reader; internal StanzaWriter? writer; internal WriteNodeFunc? write_obj = null; protected IoXmppStream(Jid remote_name) { base(remote_name); } public override async void disconnect() throws IOError { disconnected = true; if (writer == null || reader == null || stream == null) { throw new IOError.CLOSED("trying to disconnect, but no stream open"); } log.str("OUT", "", this); yield writer.write(""); reader.cancel(); yield stream.close_async(); } public void reset_stream(IOStream stream) { this.stream = stream; reader = new StanzaReader.for_stream(stream.input_stream); writer = new StanzaWriter.for_stream(stream.output_stream); writer.cancel.connect(reader.cancel); require_setup(); } public override async StanzaNode read() throws IOError { StanzaReader? reader = this.reader; if (reader == null) throw new IOError.NOT_CONNECTED("trying to read, but no stream open"); StanzaNode node = yield ((!)reader).read_node(); log.node("IN", node, this); return node; } [Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")] public override void write(StanzaNode node, int io_priority = Priority.DEFAULT) { write_async.begin(node, io_priority, null, (obj, res) => { try { write_async.end(res); } catch (Error e) { } }); } public override async void write_async(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { if (write_obj != null) { yield write_obj.write_stanza(this, node, io_priority, cancellable); } else { StanzaWriter? writer = this.writer; if (writer == null) throw new IOError.NOT_CONNECTED("trying to write, but no stream open"); log.node("OUT", node, this); yield ((!)writer).write_node(node, io_priority, cancellable); } } internal IOStream? get_stream() { return stream; } public override async void setup() throws IOError { StanzaNode outs = new StanzaNode.build("stream", "http://etherx.jabber.org/streams") .put_attribute("to", remote_name.to_string()) .put_attribute("version", "1.0") .put_attribute("xmlns", "jabber:client") .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI); outs.has_nodes = true; log.node("OUT ROOT", outs, this); write(outs); received_root_node(this, yield read_root()); setup_needed = false; } private async StanzaNode read_root() throws IOError { StanzaReader? reader = this.reader; if (reader == null) throw new IOError.NOT_CONNECTED("trying to read, but no stream open"); StanzaNode node = yield ((!)reader).read_root_node(); log.node("IN ROOT", node, this); return node; } }dino-0.4.3/xmpp-vala/src/core/module_flag.vala0000644000000000000000000000323514452563620017726 0ustar rootrootnamespace Xmpp { public class FlagIdentity : Object { public string ns { get; private set; } public string id { get; private set; } public FlagIdentity(string ns, string id) { this.ns = ns; this.id = id; } public T? cast(XmppStreamFlag flag) { return flag.get_type().is_a(typeof(T)) ? (T?) flag : null; } public bool matches(XmppStreamFlag module) { return module.get_ns() == ns && module.get_id() == id; } } public abstract class XmppStreamFlag : Object { public abstract string get_ns(); public abstract string get_id(); } public class ModuleIdentity : Object { public string ns { get; private set; } public string id { get; private set; } public ModuleIdentity(string ns, string id) { this.ns = ns; this.id = id; } public T? cast(XmppStreamModule module) { return module.get_type().is_a(typeof(T)) ? (T?) module : null; } public bool matches(XmppStreamModule module) { return module.get_ns() == ns && module.get_id() == id; } } public abstract class XmppStreamModule : Object { public abstract void attach(XmppStream stream); public abstract void detach(XmppStream stream); public abstract string get_ns(); public abstract string get_id(); } public abstract class XmppStreamNegotiationModule : XmppStreamModule { public abstract bool mandatory_outstanding(XmppStream stream); public abstract bool negotiation_active(XmppStream stream); } }dino-0.4.3/xmpp-vala/src/core/namespace_state.vala0000644000000000000000000000477514452563620020616 0ustar rootrootusing Gee; namespace Xmpp { public class NamespaceState { private HashMap uri_to_name = new HashMap (); private HashMap name_to_uri = new HashMap (); public string current_ns_uri; private NamespaceState parent; public NamespaceState() { add_assoc(XMLNS_URI, "xmlns"); add_assoc(XML_URI, "xml"); current_ns_uri = XML_URI; } public NamespaceState.for_stanza() { this(); add_assoc("http://etherx.jabber.org/streams", "stream"); current_ns_uri = "jabber:client"; } private NamespaceState.copy(NamespaceState old) { foreach (string key in old.uri_to_name.keys) { add_assoc(key, old.uri_to_name[key]); } set_current(old.current_ns_uri); } private NamespaceState.with_parent(NamespaceState parent) { this.copy(parent); this.parent = parent; } public NamespaceState.with_assoc(NamespaceState old, string ns_uri, string name) { this.copy(old); add_assoc(ns_uri, name); } public NamespaceState.with_current(NamespaceState old, string current_ns_uri) { this.copy(old); set_current(current_ns_uri); } public void add_assoc(string ns_uri, string name) { name_to_uri[name] = ns_uri; uri_to_name[ns_uri] = name; } public void set_current(string current_ns_uri) { this.current_ns_uri = current_ns_uri; } public string find_name(string ns_uri) throws IOError { if (uri_to_name.has_key(ns_uri)) { return uri_to_name[ns_uri]; } throw new IOError.INVALID_DATA(@"XML: NS URI $ns_uri not found."); } public string find_uri(string name) throws IOError { if (name_to_uri.has_key(name)) { return name_to_uri[name]; } throw new IOError.INVALID_DATA(@"XML: NS name $name not found."); } public NamespaceState push() { return new NamespaceState.with_parent(this); } public NamespaceState pop() { return parent; } public string to_string() { StringBuilder sb = new StringBuilder (); sb.append ("NamespaceState{"); foreach (string key in uri_to_name.keys) { sb.append(key); sb.append_c('='); sb.append(uri_to_name[key]); sb.append_c(','); } sb.append("current="); sb.append(current_ns_uri); sb.append_c('}'); return sb.str; } } } dino-0.4.3/xmpp-vala/src/core/stanza_attribute.vala0000644000000000000000000000530114452563620021027 0ustar rootrootnamespace Xmpp { public class StanzaAttribute : StanzaEntry { internal const string ATTRIBUTE_STRING_FORMAT = "{%s}:%s='%s'"; internal const string ATTRIBUTE_STRING_NO_NS_FORMAT = "%s='%s'"; internal const string ATTRIBUTE_STRING_ANSI_FORMAT = ANSI_COLOR_GRAY+"{%s}:"+ANSI_COLOR_END+"%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT = "%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_XML_FORMAT = "%s:%s='%s'"; internal const string ATTRIBUTE_XML_NO_NS_FORMAT = "%s='%s'"; internal const string ATTRIBUTE_XML_ANSI_FORMAT = "%s:%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_XML_ANSI_NO_NS_FORMAT = "%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal StanzaAttribute() { } public StanzaAttribute.build(string ns_uri, string name, string val) { this.ns_uri = ns_uri; this.name = name; this.val = val; } public bool equals(StanzaAttribute other) { if (other.ns_uri != ns_uri) return false; if (other.name != name) return false; if (other.val != val) return false; return true; } internal string printf(string fmt, bool no_ns = false, string? ns_name = null) { if (no_ns) { return fmt.printf(name, (!)encoded_val); } else { if (ns_name == null) { return fmt.printf((!)ns_uri, name, (!)encoded_val); } else { return fmt.printf((!)ns_name, name, (!)encoded_val); } } } public override string to_string(int i = 0) { return printf(ATTRIBUTE_STRING_FORMAT); } public string to_ansi_string(bool hide_ns = false) { if (hide_ns) { return printf(ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_STRING_ANSI_FORMAT); } } public string to_xml(NamespaceState? state_ = null) { NamespaceState state = state_ ?? new NamespaceState(); if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { return printf(ATTRIBUTE_XML_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_XML_FORMAT, false, state.find_name((!)ns_uri)); } } public string to_ansi_xml(NamespaceState? state_ = null) { NamespaceState state = state_ ?? new NamespaceState(); if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { return printf(ATTRIBUTE_XML_ANSI_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_XML_ANSI_FORMAT, false, state.find_name((!)ns_uri)); } } } } dino-0.4.3/xmpp-vala/src/core/stanza_node.vala0000644000000000000000000003642114452563620017760 0ustar rootrootusing Gee; namespace Xmpp { public abstract class StanzaEntry { protected const string ANSI_COLOR_END = "\x1b[0m"; protected const string ANSI_COLOR_GREEN = "\x1b[32m"; protected const string ANSI_COLOR_YELLOW = "\x1b[33m"; protected const string ANSI_COLOR_GRAY = "\x1b[37m"; public string? ns_uri; public string name; public string? val; public string? encoded_val { owned get { if (val == null) return null; return ((!)val).replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); } set { if (value == null) { val = null; return; } string tmp = ((!)value).replace(">", ">").replace("<", "<").replace("'","'").replace(""","\""); while (tmp.contains("&#")) { int start = tmp.index_of("&#"); int end = tmp.index_of(";", start); if (end < start) break; unichar num = -1; if (tmp[start+2]=='x') { tmp.substring(start+3, start-end-3).scanf("%x", &num); } else { num = int.parse(tmp.substring(start+2, start-end-2)); } tmp = tmp.splice(start, end, num.to_string()); } val = tmp.replace("&", "&"); } } public virtual unowned string? get_string_content() { return val; } public virtual string to_string(int i = 0) { return get_string_content() ?? "(null)"; } } public class StanzaNode : StanzaEntry { public Gee.List sub_nodes = new ArrayList(); public Gee.List attributes = new ArrayList(); public bool has_nodes = false; public bool pseudo = false; internal StanzaNode() { } public StanzaNode.build(string name, string ns_uri = "jabber:client", ArrayList? nodes = null, ArrayList? attrs = null) { this.ns_uri = ns_uri; this.name = name; if (nodes != null) this.sub_nodes.add_all((!)nodes); if (attrs != null) this.attributes.add_all((!)attrs); } public StanzaNode.text(string text) { this.name = "#text"; this.val = text; } public StanzaNode.encoded_text(string text) { this.name = "#text"; this.encoded_val = text; } public StanzaNode add_self_xmlns() { if (ns_uri == null) return this; return put_attribute("xmlns", (!)ns_uri); } public unowned string? get_attribute(string name, string? ns_uri = null) { string _name = name; string? _ns_uri = ns_uri; if (_ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var attr in attributes) { if (attr.ns_uri == (!)_ns_uri && attr.name == _name) return attr.val; } return null; } public int get_attribute_int(string name, int def = -1, string? ns_uri = null) { string? res = get_attribute(name, ns_uri); if (res == null) return def; return int.parse((!)res); } public uint get_attribute_uint(string name, uint def = 0, string? ns_uri = null) { string? res = get_attribute(name, ns_uri); if (res == null) return def; return (uint) long.parse((!)res); } public bool get_attribute_bool(string name, bool def = false, string? ns_uri = null) { string? res = get_attribute(name, ns_uri); if (res == null) return def; return ((!)res).down() == "true" || res == "1"; } public StanzaAttribute? get_attribute_raw(string name, string? ns_uri = null) { string _name = name; string? _ns_uri = ns_uri; if (_ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var attr in attributes) { if (attr.ns_uri == _ns_uri && attr.name == _name) return attr; } return null; } public Gee.List get_attributes_by_ns_uri(string ns_uri) { ArrayList ret = new ArrayList (); foreach (var attr in attributes) { if (attr.ns_uri == ns_uri) ret.add(attr); } return ret; } public unowned string? get_deep_attribute(...) { va_list l = va_list(); StanzaAttribute? res = get_deep_attribute_(va_list.copy(l)); if (res == null) return null; return ((!)res).val; } public StanzaAttribute? get_deep_attribute_(va_list l) { StanzaNode node = this; string? attribute_name = l.arg(); if (attribute_name == null) return null; while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)attribute_name); if (node_tmp == null) return null; node = (!)node_tmp; attribute_name = s; } return node.get_attribute_raw((!)attribute_name); } public StanzaNode? get_subnode(string name, string? ns_uri = null, bool recurse = false) { string _name = name; string? _ns_uri = ns_uri; if (ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var node in sub_nodes) { if (node.ns_uri == _ns_uri && node.name == _name) return node; if (recurse) { var x = node.get_subnode(_name, _ns_uri, recurse); if (x != null) return x; } } return null; } public Gee.List get_subnodes(string name, string? ns_uri = null, bool recurse = false) { ArrayList ret = new ArrayList(); string _name = name; string? _ns_uri = ns_uri; if (ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var node in sub_nodes) { if (node.ns_uri == _ns_uri && node.name == _name) ret.add(node); if (recurse) { ret.add_all(node.get_subnodes(_name, _ns_uri, recurse)); } } return ret; } public StanzaNode? get_deep_subnode(...) { va_list l = va_list(); return get_deep_subnode_(va_list.copy(l)); } public StanzaNode? get_deep_subnode_(va_list l) { StanzaNode node = this; while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)s); if (node_tmp == null) return null; node = (!)node_tmp; } return node; } public Gee.List get_deep_subnodes(...) { va_list l = va_list(); return get_deep_subnodes_(va_list.copy(l)); } public Gee.List get_deep_subnodes_(va_list l) { StanzaNode node = this; string? subnode_name = l.arg(); if (subnode_name == null) return new ArrayList(); while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)subnode_name); if (node_tmp == null) return new ArrayList(); node = (!)node_tmp; subnode_name = s; } return node.get_subnodes((!)subnode_name); } public Gee.List get_all_subnodes() { return sub_nodes; } public Gee.List get_deep_all_subnodes(...) { va_list l = va_list(); StanzaNode? node = get_deep_subnode_(va_list.copy(l)); if (node != null) return ((!)node).get_all_subnodes(); return new ArrayList(); } public void add_attribute(StanzaAttribute attr) { attributes.add(attr); } public override unowned string? get_string_content() { if (val != null) return val; if (sub_nodes.size == 1) return sub_nodes[0].get_string_content(); return null; } public unowned string? get_deep_string_content(...) { va_list l = va_list(); StanzaNode? node = get_deep_subnode_(va_list.copy(l)); if (node != null) return ((!)node).get_string_content(); return null; } public StanzaNode put_attribute(string name, string val, string? ns_uri = null) { string? _ns_uri = ns_uri; if (name == "xmlns") _ns_uri = XMLNS_URI; if (_ns_uri == null) _ns_uri = this.ns_uri; if (_ns_uri == null) return this; attributes.add(new StanzaAttribute.build((!)_ns_uri, name, val)); return this; } /** * Set only occurrence **/ public void set_attribute(string name, string val, string? ns_uri = null) { if (ns_uri == null) ns_uri = this.ns_uri; foreach (var attr in attributes) { if (attr.ns_uri == ns_uri && attr.name == name) { attr.val = val; return; } } put_attribute(name, val, ns_uri); } public StanzaNode put_node(StanzaNode node) { sub_nodes.add(node); return this; } public bool equals(StanzaNode other) { if (other.name != name) return false; if (other.val != val) return false; if (name == "#text") return true; if (other.ns_uri != ns_uri) return false; if (other.sub_nodes.size != sub_nodes.size) return false; for (int i = 0; i < sub_nodes.size; i++) { if (!other.sub_nodes[i].equals(sub_nodes[i])) return false; } if (other.attributes.size != attributes.size) return false; for (int i = 0; i < attributes.size; i++) { if (!other.attributes[i].equals(attributes[i])) return false; } return true; } private const string TAG_START_BEGIN_FORMAT = "%s<{%s}:%s"; private const string TAG_START_EMPTY_END = " />\n"; private const string TAG_START_CONTENT_END = ">\n"; private const string TAG_END_FORMAT = "%s\n"; private const string TAG_ANSI_START_BEGIN_FORMAT = "%s"+ANSI_COLOR_YELLOW+"<"+ANSI_COLOR_GRAY+"{%s}:"+ANSI_COLOR_YELLOW+"%s"+ANSI_COLOR_END; private const string TAG_ANSI_START_BEGIN_NO_NS_FORMAT = "%s"+ANSI_COLOR_YELLOW+"<%s"+ANSI_COLOR_END; private const string TAG_ANSI_START_EMPTY_END = ANSI_COLOR_YELLOW+" />"+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_START_CONTENT_END = ANSI_COLOR_YELLOW+">"+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_END_FORMAT = "%s"+ANSI_COLOR_YELLOW+""+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_END_NO_NS_FORMAT = "%s"+ANSI_COLOR_YELLOW+""+ANSI_COLOR_END+"\n"; internal string printf(int i, string fmt_start_begin, string start_empty_end, string start_content_end, string fmt_end, string fmt_attr, bool no_ns = false) { string indent = string.nfill (i * 2, ' '); if (name == "#text") { if (((!)val).length > 1000) { return indent + "[... retracted for brevity ...]\n"; } return indent + ((!)val).replace("\n", indent + "\n") + "\n"; } var sb = new StringBuilder(); if (no_ns) { sb.append_printf(fmt_start_begin, indent, name); } else { sb.append_printf(fmt_start_begin, indent, (!)ns_uri, name); } foreach (StanzaAttribute attr in attributes) { sb.append_printf(" %s", attr.printf(fmt_attr, no_ns)); } if (!has_nodes && sub_nodes.size == 0) { sb.append(start_empty_end); } else { sb.append(start_content_end); if (sub_nodes.size != 0) { foreach (StanzaNode subnode in sub_nodes) { sb.append(subnode.printf(i+1, fmt_start_begin, start_empty_end, start_content_end, fmt_end, fmt_attr, no_ns)); } if (no_ns) { sb.append_printf(fmt_end, indent, name); } else { sb.append_printf(fmt_end, indent, (!)ns_uri, name); } } } return sb.str; } public override string to_string(int i = 0) { return printf(i, TAG_START_BEGIN_FORMAT, TAG_START_EMPTY_END, TAG_START_CONTENT_END, TAG_END_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_FORMAT); } public string to_ansi_string(bool hide_ns = false, int i = 0) { if (hide_ns) { return printf(i, TAG_ANSI_START_BEGIN_NO_NS_FORMAT, TAG_ANSI_START_EMPTY_END, TAG_ANSI_START_CONTENT_END, TAG_ANSI_END_NO_NS_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT, true); } else { return printf(i, TAG_ANSI_START_BEGIN_FORMAT, TAG_ANSI_START_EMPTY_END, TAG_ANSI_START_CONTENT_END, TAG_ANSI_END_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_ANSI_FORMAT); } } public string to_xml(NamespaceState? state = null) throws IOError { NamespaceState my_state = state ?? new NamespaceState.for_stanza(); if (name == "#text") return val == null ? "" : (!)encoded_val; my_state = my_state.push(); foreach (var xmlns in get_attributes_by_ns_uri (XMLNS_URI)) { if (xmlns.val == null) continue; if (xmlns.name == "xmlns") { my_state.set_current((!)xmlns.val); } else { my_state.add_assoc((!)xmlns.val, xmlns.name); } } var sb = new StringBuilder(); if (ns_uri == my_state.current_ns_uri) { sb.append_printf("<%s", name); } else { sb.append_printf("<%s:%s", my_state.find_name ((!)ns_uri), name); } var attr_ns_state = new NamespaceState.with_current(my_state, (!)ns_uri); foreach (StanzaAttribute attr in attributes) { sb.append_printf(" %s", attr.to_xml(attr_ns_state)); } if (!has_nodes && sub_nodes.size == 0) { sb.append("/>"); } else { sb.append(">"); if (sub_nodes.size != 0) { foreach (StanzaNode subnode in sub_nodes) { sb.append(subnode.to_xml(my_state)); } if (ns_uri == my_state.current_ns_uri) { sb.append(@""); } else { sb.append_printf("", my_state.find_name ((!)ns_uri), name); } } } my_state = my_state.pop(); return sb.str; } } } dino-0.4.3/xmpp-vala/src/core/stanza_reader.vala0000644000000000000000000002350714452563620020276 0ustar rootrootusing Gee; namespace Xmpp { public const string XMLNS_URI = "http://www.w3.org/2000/xmlns/"; public const string XML_URI = "http://www.w3.org/XML/1998/namespace"; public const string JABBER_URI = "jabber:client"; public class StanzaReader { private static int BUFFER_MAX = 4096; private InputStream? input; private uint8[] buffer; private int buffer_fill = 0; private int buffer_pos = 0; private Cancellable cancellable = new Cancellable(); private NamespaceState ns_state = new NamespaceState(); public StanzaReader.for_buffer(uint8[] buffer) { this.buffer = buffer; this.buffer_fill = buffer.length; } public StanzaReader.for_string(string s) { this.for_buffer(s.data); } public StanzaReader.for_stream(InputStream input) { this.input = input; buffer = new uint8[BUFFER_MAX]; } public void cancel() { cancellable.cancel(); } private async void update_buffer() throws IOError { InputStream? input = this.input; if (input == null) throw new IOError.CLOSED("No input stream specified and end of buffer reached."); if (cancellable.is_cancelled()) throw new IOError.CANCELLED("Input stream is canceled."); buffer_fill = (int) yield ((!)input).read_async(buffer, GLib.Priority.DEFAULT, cancellable); if (buffer_fill == 0) throw new IOError.CLOSED("End of input stream reached."); buffer_pos = 0; } private async char read_single() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } return (char) buffer[buffer_pos++]; } private async char peek_single() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } return (char) buffer[buffer_pos]; } private bool is_ws(uint8 what) { return what == ' ' || what == '\t' || what == '\r' || what == '\n'; } private void skip_single() { buffer_pos++; } private async void skip_until_non_ws() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (is_ws(buffer[buffer_pos])) { buffer_pos++; if (buffer_pos >= buffer_fill) { yield update_buffer(); } } } private async string read_until_ws() throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (!is_ws(buffer[buffer_pos])) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async string read_until_char_or_ws(char x, char y = 0) throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (buffer[buffer_pos] != x && buffer[buffer_pos] != y && !is_ws(buffer[buffer_pos])) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async string read_until_char(char x) throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (buffer[buffer_pos] != x) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async StanzaAttribute read_attribute() throws IOError { var res = new StanzaAttribute(); res.name = yield read_until_char_or_ws('='); if ((yield read_single()) == '=') { var quot = yield peek_single(); if (quot == '\'' || quot == '"') { skip_single(); res.encoded_val = yield read_until_char(quot); skip_single(); } else { res.encoded_val = yield read_until_ws(); } } return res; } private void handle_entry_ns(StanzaEntry entry, string default_uri = ns_state.current_ns_uri) throws IOError { if (entry.ns_uri != null) return; if (entry.name.contains(":")) { var split = entry.name.split(":"); entry.ns_uri = ns_state.find_uri(split[0]); entry.name = split[1]; } else { entry.ns_uri = default_uri; } } private void handle_stanza_ns(StanzaNode res) throws IOError { foreach (StanzaAttribute attr in res.attributes) { if (attr.name == "xmlns" && attr.val != null) { attr.ns_uri = XMLNS_URI; ns_state.set_current((!)attr.val); } else if (attr.name.contains(":") && attr.val != null) { var split = attr.name.split(":"); if (split[0] == "xmlns") { attr.ns_uri = XMLNS_URI; attr.name = split[1]; ns_state.add_assoc((!)attr.val, attr.name); } } } handle_entry_ns(res); foreach (StanzaAttribute attr in res.attributes) { handle_entry_ns(attr, res.ns_uri ?? ns_state.current_ns_uri); } } public async StanzaNode read_node_start() throws IOError { var res = new StanzaNode(); res.attributes = new ArrayList(); var eof = false; if ((yield peek_single()) == '<') skip_single(); if ((yield peek_single()) == '?') res.pseudo = true; if ((yield peek_single()) == '/') { eof = true; skip_single(); res.name = yield read_until_char_or_ws('>'); while ((yield peek_single()) != '>') { skip_single(); } skip_single(); res.has_nodes = false; res.pseudo = false; handle_stanza_ns(res); return res; } res.name = yield read_until_char_or_ws('>', '/'); yield skip_until_non_ws(); char next_char = yield peek_single(); while (next_char != '/' && next_char != '>' && next_char != '?') { res.attributes.add(yield read_attribute()); yield skip_until_non_ws(); next_char = yield peek_single(); } if ((yield read_single()) == '/' || res.pseudo) { res.has_nodes = false; skip_single(); } else { res.has_nodes = true; } handle_stanza_ns(res); return res; } public async StanzaNode read_text_node() throws IOError { var res = new StanzaNode(); res.name = "#text"; res.ns_uri = ns_state.current_ns_uri; res.encoded_val = (yield read_until_char('<')); return res; } public async StanzaNode read_root_node() throws IOError { yield skip_until_non_ws(); if ((yield peek_single()) == '<') { var res = yield read_node_start(); if (res.pseudo) { return yield read_root_node(); } return res; } else { throw new IOError.INVALID_DATA("XML: Content before root node"); } } public async StanzaNode read_stanza_node() throws IOError { try { ns_state = ns_state.push(); var res = yield read_node_start(); if (res.has_nodes) { bool finish_node_seen = false; StanzaNode? text_node = null; do { text_node = yield read_text_node(); if ((yield peek_single()) == '<') { skip_single(); if ((yield peek_single()) == '/') { skip_single(); string desc = yield read_until_char('>'); skip_single(); if (desc.contains(":")) { var split = desc.split(":"); if (split[0] != ns_state.find_name((!)res.ns_uri)) throw new IOError.INVALID_DATA("XML: Closing namespace prefix mismatch"); if (split[1] != res.name) throw new IOError.INVALID_DATA("XML: Closing element name mismatch"); } else { if (ns_state.current_ns_uri != res.ns_uri) throw new IOError.INVALID_DATA("XML: Closing element namespace mismatch"); if (desc != res.name) throw new IOError.INVALID_DATA("XML: Closing element name mismatch"); } finish_node_seen = true; } else { res.sub_nodes.add(yield read_stanza_node()); } } } while (!finish_node_seen); if (res.sub_nodes.size == 0) { if (text_node == null || text_node.val.length == 0) { res.has_nodes = false; } else { res.sub_nodes.add(text_node); } } } ns_state = ns_state.pop(); return res; } catch (IOError.INVALID_DATA e) { uint8[] buffer_cpy = new uint8[buffer.length + 1]; Memory.copy(buffer_cpy, buffer, buffer.length); warning("invalid data at: %s".printf((string)buffer_cpy) + "\n"); throw e; } } public async StanzaNode read_node() throws IOError { yield skip_until_non_ws(); if ((yield peek_single()) == '<') { return yield read_stanza_node(); } else { return yield read_text_node(); } } } } dino-0.4.3/xmpp-vala/src/core/stanza_writer.vala0000644000000000000000000000432514452563620020345 0ustar rootrootnamespace Xmpp { public class StanzaWriter { public signal void cancel(); private OutputStream output; private Queue queue = new Queue(); private bool running = false; public StanzaWriter.for_stream(OutputStream output) { this.output = output; } public async void write_node(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { yield write_data(node.to_xml().data, io_priority, cancellable); } public async void write_nodes(StanzaNode node1, StanzaNode node2, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { var data1 = node1.to_xml().data; var data2 = node2.to_xml().data; uint8[] concat = new uint8[data1.length + data2.length]; int i = 0; foreach (var datum in data1) { concat[i++] = datum; } foreach (var datum in data2) { concat[i++] = datum; } yield write_data(concat, io_priority, cancellable); } public async void write(string s, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { yield write_data(s.data, io_priority, cancellable); } private async void write_data(owned uint8[] data, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { if (running) { queue.push_tail(new SourceFuncWrapper(write_data.callback)); yield; } running = true; try { yield output.write_all_async(data, io_priority, cancellable, null); } catch (IOError e) { cancel(); throw e; } catch (GLib.Error e) { cancel(); throw new IOError.FAILED("Error in GLib: %s".printf(e.message)); } finally { SourceFuncWrapper? sfw = queue.pop_head(); if (sfw != null) { sfw.sfun(); } else { running = false; } } } } public class SourceFuncWrapper : Object { public SourceFunc sfun; public SourceFuncWrapper(owned SourceFunc sfun) { this.sfun = (owned)sfun; } } } dino-0.4.3/xmpp-vala/src/core/starttls_xmpp_stream.vala0000644000000000000000000000413014452563620021742 0ustar rootrootpublic class Xmpp.StartTlsXmppStream : TlsXmppStream { private const string TLS_NS_URI = "urn:ietf:params:xml:ns:xmpp-tls"; string host; uint16 port; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert; public StartTlsXmppStream(Jid remote, string host, uint16 port, TlsXmppStream.OnInvalidCertWrapper on_invalid_cert) { base(remote); this.host = host; this.port = port; this.on_invalid_cert = on_invalid_cert; } public override async void connect() throws IOError { try { SocketClient client = new SocketClient(); debug("Connecting to %s:%i (starttls)", host, port); IOStream stream = yield client.connect_to_host_async(host, port); reset_stream(stream); yield setup(); StanzaNode node = yield read(); var starttls_node = node.get_subnode("starttls", TLS_NS_URI); if (starttls_node == null) { warning("%s does not offer starttls", remote_name.to_string()); } write(new StanzaNode.build("starttls", TLS_NS_URI).add_self_xmlns()); node = yield read(); if (node.ns_uri != TLS_NS_URI || node.name != "proceed") { warning("Server did not 'proceed' starttls request"); } try { var identity = new NetworkService("xmpp-client", "tcp", remote_name.to_string()); var conn = TlsClientConnection.new(get_stream(), identity); reset_stream(conn); conn.accept_certificate.connect(on_invalid_certificate); conn.accept_certificate.connect((cert, flags) => on_invalid_cert.func(cert, flags)); } catch (Error e) { stderr.printf("Failed to start TLS: %s\n", e.message); } yield setup(); attach_negotation_modules(); } catch (IOError e) { throw e; } catch (Error e) { throw new IOError.CONNECTION_REFUSED("Failed connecting to %s:%i (starttls): %s", host, port, e.message); } } } dino-0.4.3/xmpp-vala/src/core/stream_connect.vala0000644000000000000000000000766114452563620020463 0ustar rootrootnamespace Xmpp { private class SrvTargetInfo { public string host { get; set; } public uint16 port { get; set; } public string service { get; set; } public uint16 priority { get; set; } } public class XmppStreamResult { public TlsXmppStream? stream { get; set; } public TlsCertificateFlags? tls_errors { get; set; } public IOError? io_error { get; set; } } public async XmppStreamResult establish_stream(Jid bare_jid, Gee.List modules, string? log_options, owned TlsXmppStream.OnInvalidCert on_invalid_cert) { Jid remote = bare_jid.domain_jid; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert_wrapper = new TlsXmppStream.OnInvalidCertWrapper(on_invalid_cert); //Lookup xmpp-client and xmpps-client SRV records GLib.List? targets = new GLib.List(); GLibFixes.Resolver resolver = GLibFixes.Resolver.get_default(); try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpp-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpp-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpp-client DNS records for %s: %s", remote.to_string(), e.message); } try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpps-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpps-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpps-client DNS records for %s: %s", remote.to_string(), e.message); } targets.sort((a, b) => { return a.priority - b.priority; }); // Add fallback connection bool should_add_fallback = true; foreach (SrvTargetInfo target in targets) { if (target.service == "xmpp-client" && target.port == 5222 && target.host == remote.to_string()) { should_add_fallback = false; } } if (should_add_fallback) { targets.append(new SrvTargetInfo() { host=remote.to_string(), port=5222, service="xmpp-client", priority=uint16.MAX}); } // Try all connection options from lowest to highest priority TlsXmppStream? stream = null; TlsCertificateFlags? tls_errors = null; IOError? io_error = null; foreach (SrvTargetInfo target in targets) { try { if (target.service == "xmpp-client") { stream = new StartTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } else { stream = new DirectTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } stream.log = new XmppLog(bare_jid.to_string(), log_options); foreach (XmppStreamModule module in modules) { stream.add_module(module); } yield stream.connect(); return new XmppStreamResult() { stream=stream }; } catch (IOError e) { warning("Could not establish XMPP session with %s:%i: %s", target.host, target.port, e.message); if (stream != null) { if (stream.errors != null) { tls_errors = stream.errors; } io_error = e; stream.detach_modules(); } } } return new XmppStreamResult() { io_error=io_error, tls_errors=tls_errors }; } }dino-0.4.3/xmpp-vala/src/core/tls_xmpp_stream.vala0000644000000000000000000000226414452563620020672 0ustar rootrootpublic abstract class Xmpp.TlsXmppStream : IoXmppStream { public TlsCertificateFlags? errors; public delegate bool OnInvalidCert(GLib.TlsCertificate peer_cert, GLib.TlsCertificateFlags errors); public class OnInvalidCertWrapper { public OnInvalidCert func; public OnInvalidCertWrapper(owned OnInvalidCert func) { this.func = (owned) func; } } protected TlsXmppStream(Jid remote_name) { base(remote_name); } protected bool on_invalid_certificate(TlsCertificate peer_cert, TlsCertificateFlags errors) { this.errors = errors; string error_str = ""; foreach (var f in new TlsCertificateFlags[]{TlsCertificateFlags.UNKNOWN_CA, TlsCertificateFlags.BAD_IDENTITY, TlsCertificateFlags.NOT_ACTIVATED, TlsCertificateFlags.EXPIRED, TlsCertificateFlags.REVOKED, TlsCertificateFlags.INSECURE, TlsCertificateFlags.GENERIC_ERROR, TlsCertificateFlags.VALIDATE_ALL}) { if (f in errors) { error_str += @"$(f), "; } } warning(@"[%p, %s] Tls Certificate Errors: %s", this, this.remote_name.to_string(), error_str); return false; } }dino-0.4.3/xmpp-vala/src/core/xmpp_log.vala0000644000000000000000000001144314452563620017275 0ustar rootrootusing Gee; namespace Xmpp { public class XmppLog { protected const string ANSI_COLOR_END = "\x1b[0m"; protected const string ANSI_COLOR_WHITE = "\x1b[37;1m"; class NodeLogDesc { public string? name; private string? ns_uri; private string? val; private Map attrs = new HashMap(); private NodeLogDesc? inner; public NodeLogDesc(string desc) { string d = desc; if (d.contains("[")) { int start = d.index_of("["); int end = d.index_of("]"); string attrs = d.substring(start + 1, end - start - 1); d = d.substring(0, start) + d.substring(end + 1); foreach (string attr in attrs.split(",")) { if (attr.contains("=")) { string key = attr.substring(0, attr.index_of("=")); string val = attr.substring(attr.index_of("=") + 1); this.attrs[key] = val; } else { this.attrs[attr] = null; } } } if (d.contains(":") && d.index_of("{") == 0 && d.index_of("}") != -1) { int end = d.index_of("}"); this.ns_uri = d.substring(1, end - 1); d = d.substring(end + 2); } if (d.contains(".")) { inner = new NodeLogDesc(d.substring(d.index_of(".") + 1)); d = d.substring(0, d.index_of(".")); } else if (d.contains("=")) { this.val = d.substring(d.index_of("=")); d = d.substring(0, d.index_of("=")); } if (d != "") this.name = d; } public bool matches(StanzaNode node) { if (name != null && node.name != name) return false; if (ns_uri != null && node.ns_uri != ns_uri) return false; if (val != null && node.val != val) return false; foreach (var pair in attrs.entries) { if (pair.value == null && node.get_attribute(pair.key) == null) return false; else if (pair.value != null && pair.value != node.get_attribute(pair.key)) return false; } if (inner == null) return true; foreach (StanzaNode snode in node.get_all_subnodes()) { if (((!)inner).matches(snode)) return true; } return false; } } private bool use_ansi; private bool hide_ns = true; private string ident; private string desc; private Gee.List descs = new ArrayList(); public XmppLog(string? ident = null, string? desc = null) { this.ident = ident ?? ""; this.desc = desc ?? ""; this.use_ansi = is_atty(stderr.fileno()); while (this.desc.contains(";")) { string opt = this.desc.substring(0, this.desc.index_of(";")); this.desc = this.desc.substring(opt.length + 1); switch (opt) { case "ansi": use_ansi = true; break; case "no-ansi": use_ansi = false; break; case "hide-ns": hide_ns = true; break; case "show-ns": hide_ns = false; break; } } if (desc != "") { foreach (string d in this.desc.split("|")) { descs.add(new NodeLogDesc(d)); } } } public virtual bool should_log_node(StanzaNode node) { if (ident == "" || desc == "") return false; if (desc == "all") return true; foreach (var desc in descs) { if (desc.matches(node)) return true; } return false; } public virtual bool should_log_str(string str) { if (ident == "" || desc == "") return false; if (desc == "all") return true; foreach (var desc in descs) { if (desc.name == "#text") return true; } return false; } public void node(string what, StanzaNode node, XmppStream stream) { if (should_log_node(node)) { stderr.printf("%sXMPP %s [%s stream:%p thread:%p %s]%s\n%s\n", use_ansi ? ANSI_COLOR_WHITE : "", what, ident, stream, Thread.self(), new DateTime.now_local().to_string(), use_ansi ? ANSI_COLOR_END : "", use_ansi ? node.to_ansi_string(hide_ns) : node.to_string()); } } public void str(string what, string str, XmppStream stream) { if (should_log_str(str)) { stderr.printf("%sXMPP %s [%s stream:%p thread:%p %s]%s\n%s\n", use_ansi ? ANSI_COLOR_WHITE : "", what, ident, stream, Thread.self(), new DateTime.now_local().to_string(), use_ansi ? ANSI_COLOR_END : "", str); } } [CCode (cname = "isatty")] private static extern bool is_atty(int fd); } } dino-0.4.3/xmpp-vala/src/core/xmpp_stream.vala0000644000000000000000000001515014452563620020006 0ustar rootrootusing Gee; public abstract class Xmpp.XmppStream : Object { public signal void received_node(XmppStream stream, StanzaNode node); public signal void received_root_node(XmppStream stream, StanzaNode node); public signal void received_features_node(XmppStream stream); public signal void received_message_stanza(XmppStream stream, StanzaNode node); public signal void received_presence_stanza(XmppStream stream, StanzaNode node); public signal void received_iq_stanza(XmppStream stream, StanzaNode node); public signal void received_nonza(XmppStream stream, StanzaNode node); public signal void stream_negotiated(XmppStream stream); public signal void attached_modules(XmppStream stream); public const string NS_URI = "http://etherx.jabber.org/streams"; public Gee.List flags { get; private set; default=new ArrayList(); } public Gee.List modules { get; private set; default=new ArrayList(); } public StanzaNode? features { get; private set; default = new StanzaNode.build("features", NS_URI); } public Jid remote_name; public XmppLog log = new XmppLog(); public bool negotiation_complete { get; set; default=false; } protected bool non_negotiation_modules_attached = false; protected bool setup_needed = false; protected bool disconnected = false; protected XmppStream(Jid remote_name) { this.remote_name = remote_name; } public abstract async void connect() throws IOError; public abstract async void disconnect() throws IOError; public abstract async StanzaNode read() throws IOError; [Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")] public abstract void write(StanzaNode node, int io_priority = Priority.DEFAULT); public abstract async void write_async(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError; public abstract async void setup() throws IOError; public void require_setup() { setup_needed = true; } public bool is_setup_needed() { return setup_needed; } public void add_flag(XmppStreamFlag flag) { flags.add(flag); } public bool has_flag(FlagIdentity? identity) { return get_flag(identity) != null; } public T? get_flag(FlagIdentity? identity) { if (identity == null) return null; foreach (var flag in flags) { if (((!)identity).matches(flag)) return ((!)identity).cast(flag); } return null; } public void remove_flag(XmppStreamFlag flag) { flags.remove(flag); } public XmppStream add_module(XmppStreamModule module) { foreach (XmppStreamModule m in modules) { if (m.get_ns() == module.get_ns() && m.get_id() == module.get_id()) { warning("[%p] Adding already added module: %s\n", this, module.get_id()); return this; } } modules.add(module); if (negotiation_complete) module.attach(this); return this; } public void detach_modules() { foreach (XmppStreamModule module in modules) { module.detach(this); } } public T? get_module(ModuleIdentity? identity) { if (identity == null) return null; foreach (var module in modules) { if (((!)identity).matches(module)) return ((!)identity).cast(module); } return null; } public async void loop() throws IOError { while (true) { if (setup_needed) { yield setup(); } StanzaNode node = yield read(); Idle.add(loop.callback); yield; if (disconnected) break; yield handle_stanza(node); if (!non_negotiation_modules_attached && negotiation_modules_done()) { attach_non_negotation_modules(); non_negotiation_modules_attached = true; if (!negotiation_complete) { stream_negotiated(this); negotiation_complete = true; } } } } private async void handle_stanza(StanzaNode node) { received_node(this, node); if (node.ns_uri == NS_URI && node.name == "features") { features = node; received_features_node(this); } else if (node.ns_uri == NS_URI && node.name == "stream" && node.pseudo) { debug("[%p] Server closed stream", this); try { yield disconnect(); } catch (Error e) {} return; } else if (node.ns_uri == JABBER_URI) { if (node.name == "message") { received_message_stanza(this, node); } else if (node.name == "presence") { received_presence_stanza(this, node); } else if (node.name == "iq") { received_iq_stanza(this, node); } else { received_nonza(this, node); } } else { received_nonza(this, node); } } public bool is_negotiation_active() { foreach (XmppStreamModule module in modules) { if (module is XmppStreamNegotiationModule) { XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module; if (negotiation_module.negotiation_active(this)) return true; } } return false; } private bool negotiation_modules_done() throws IOError { if (setup_needed) return false; if (is_negotiation_active()) return false; foreach (XmppStreamModule module in modules) { if (module is XmppStreamNegotiationModule) { XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module; if (negotiation_module.mandatory_outstanding(this)) { throw new IOError.FAILED("mandatory-to-negotiate feature not negotiated: " + negotiation_module.get_id()); } } } return true; } private void attach_non_negotation_modules() { foreach (XmppStreamModule module in modules) { if (module as XmppStreamNegotiationModule == null) { module.attach(this); } } attached_modules(this); } public void attach_negotation_modules() { foreach (XmppStreamModule module in modules) { if (module as XmppStreamNegotiationModule != null) { module.attach(this); } } } }dino-0.4.3/xmpp-vala/src/glib_fixes.vapi0000644000000000000000000000414714452563620016652 0ustar rootroot[CCode (cprefix = "G", gir_namespace = "Gio", gir_version = "2.0", lower_case_cprefix = "g_")] namespace GLibFixes { [CCode (cheader_filename = "gio/gio.h", type_id = "g_resolver_get_type ()")] public class Resolver : GLib.Object { [CCode (has_construct_function = false)] protected Resolver(); [Version (since = "2.22")] public static Resolver get_default(); [Version (since = "2.22")] public virtual string lookup_by_address(GLib.InetAddress address, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual async string lookup_by_address_async(GLib.InetAddress address, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual GLib.List lookup_by_name(string hostname, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual async GLib.List lookup_by_name_async(string hostname, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.34")] public virtual GLib.List lookup_records(string rrname, GLib.ResolverRecordType record_type, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.34")] public virtual async GLib.List lookup_records_async(string rrname, GLib.ResolverRecordType record_type, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual GLib.List lookup_service(string service, string protocol, string domain, GLib.Cancellable? cancellable = null) throws GLib.Error ; [CCode (finish_vfunc_name = "lookup_service_finish", vfunc_name = "lookup_service_async")] public async GLib.List lookup_service_async (string service, string protocol, string domain, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "2.22")] public void set_default(); public virtual signal void reload (); } } dino-0.4.3/xmpp-vala/src/module/0000755000000000000000000000000014452563620015135 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/bind.vala0000644000000000000000000000633014452563620016720 0ustar rootrootnamespace Xmpp.Bind { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-bind"; /** The parties to a stream MUST consider resource binding as mandatory-to-negotiate. (RFC6120 7.3.1) */ public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "bind_module"); public string? requested_resource { get; set; } public signal void bound_to_resource(XmppStream stream, Jid my_jid); public Module(string? requested_resource) { this.requested_resource = requested_resource; } public void iq_response_stanza(XmppStream stream, Iq.Stanza iq) { var flag = stream.get_flag(Flag.IDENTITY); if (flag == null || flag.finished) return; if (iq.type_ == Iq.Stanza.TYPE_RESULT) { try { flag.my_jid = new Jid(iq.stanza.get_subnode("jid", NS_URI, true).get_string_content()); flag.finished = true; bound_to_resource(stream, flag.my_jid); } catch (InvalidJidError e) { warning("Received invalid Jid when binding: %s", e.message); } } } public void received_features_node(XmppStream stream) { if (stream.is_setup_needed()) return; if (stream.is_negotiation_active()) return; var bind = stream.features.get_subnode("bind", NS_URI); if (bind != null) { var flag = new Flag(); StanzaNode bind_node = new StanzaNode.build("bind", NS_URI).add_self_xmlns(); if (requested_resource != null) { bind_node.put_node(new StanzaNode.build("resource", NS_URI).put_node(new StanzaNode.text(requested_resource))); } stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.set(bind_node), iq_response_stanza); stream.add_flag(flag); } } public override void attach(XmppStream stream) { stream.received_features_node.connect(this.received_features_node); } public override void detach(XmppStream stream) { stream.received_features_node.disconnect(this.received_features_node); } public override bool mandatory_outstanding(XmppStream stream) { return !stream.has_flag(Flag.IDENTITY) || !stream.get_flag(Flag.IDENTITY).finished; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "bind"); public Jid? my_jid; public bool finished = false; public static Jid? get_my_jid(XmppStream stream) { return stream.get_flag(IDENTITY).my_jid; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/bookmarks_provider.vala0000644000000000000000000000143514452563620021707 0ustar rootrootusing Gee; namespace Xmpp { public interface BookmarksProvider : Object { public signal void conference_added(XmppStream stream, Conference conferences); public signal void conference_removed(XmppStream stream, Jid jid); public signal void conference_changed(XmppStream stream, Conference conferences); public signal void received_conferences(XmppStream stream, Set conferences); public async abstract async Set? get_conferences(XmppStream stream); public async abstract void add_conference(XmppStream stream, Conference conference); public async abstract void remove_conference(XmppStream stream, Conference conference); public async abstract void replace_conference(XmppStream stream, Jid muc_jid, Conference modified_conference); } } dino-0.4.3/xmpp-vala/src/module/conference.vala0000644000000000000000000000110714452563620020110 0ustar rootrootnamespace Xmpp { public class Conference : Object { public virtual Jid? jid { get; set; } public virtual bool autojoin { get; set; } public virtual string? nick { get; set; } public virtual string? name { get; set; } public virtual string? password { get; set; } public bool equals(Conference c) { return equals_func(this, c); } public static bool equals_func(Conference a, Conference b) { return Jid.equals_func(a.jid, b.jid); } public static uint hash_func(Conference a) { return Jid.hash_func(a.jid); } } } dino-0.4.3/xmpp-vala/src/module/iq/0000755000000000000000000000000014452563620015546 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/iq/module.vala0000644000000000000000000001266314452563620017710 0ustar rootrootusing Gee; namespace Xmpp.Iq { private const string NS_URI = "jabber:client"; public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "iq_module"); public signal void preprocess_incoming_iq_set_get(XmppStream stream, Stanza iq_stanza); public signal void preprocess_outgoing_iq_set_get(XmppStream stream, Stanza iq_stanza); private HashMap responseListeners = new HashMap(); private HashMap> namespaceRegistrants = new HashMap>(); public async Iq.Stanza send_iq_async(XmppStream stream, Iq.Stanza iq, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { assert(iq.type_ == Iq.Stanza.TYPE_GET || iq.type_ == Iq.Stanza.TYPE_SET); preprocess_outgoing_iq_set_get(stream, iq); Iq.Stanza? return_stanza = null; responseListeners[iq.id] = new ResponseListener((_, result_iq) => { return_stanza = result_iq; Idle.add(send_iq_async.callback); }); stream.write_async(iq.stanza, io_priority, cancellable); yield; cancellable.set_error_if_cancelled(); return return_stanza; } public delegate void OnResult(XmppStream stream, Iq.Stanza iq); public void send_iq(XmppStream stream, Iq.Stanza iq, owned OnResult? listener = null, int io_priority = Priority.DEFAULT) { preprocess_outgoing_iq_set_get(stream, iq); stream.write(iq.stanza, io_priority); if (listener != null) { responseListeners[iq.id] = new ResponseListener((owned) listener); } } public void register_for_namespace(string namespace, Handler module) { if (!namespaceRegistrants.has_key(namespace)) { namespaceRegistrants.set(namespace, new ArrayList()); } namespaceRegistrants[namespace].add(module); } public void unregister_from_namespace(string namespace, Handler module) { ArrayList? handlers = namespaceRegistrants[namespace]; if (handlers != null) handlers.remove(module); } public override void attach(XmppStream stream) { stream.received_iq_stanza.connect(on_received_iq_stanza); } public override void detach(XmppStream stream) { stream.received_iq_stanza.disconnect(on_received_iq_stanza); } public override bool mandatory_outstanding(XmppStream stream) { return false; } public override bool negotiation_active(XmppStream stream) { return false; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private async void on_received_iq_stanza(XmppStream stream, StanzaNode node) { Iq.Stanza iq = new Iq.Stanza.from_stanza(node, stream.has_flag(Bind.Flag.IDENTITY) ? stream.get_flag(Bind.Flag.IDENTITY).my_jid : null); if (iq.type_ == Iq.Stanza.TYPE_RESULT || iq.is_error()) { if (responseListeners.has_key(iq.id)) { ResponseListener? listener = responseListeners.get(iq.id); if (listener != null) { listener.on_result(stream, iq); } responseListeners.unset(iq.id); } } else { Gee.List children = node.get_all_subnodes(); if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) { preprocess_incoming_iq_set_get(stream, iq); Gee.List handlers = namespaceRegistrants[children[0].ns_uri]; foreach (Handler handler in handlers) { if (iq.type_ == Iq.Stanza.TYPE_GET) { yield handler.on_iq_get(stream, iq); } else if (iq.type_ == Iq.Stanza.TYPE_SET) { yield handler.on_iq_set(stream, iq); } } } else { // Send error if we don't handle the NS of the IQ get/set payload (RFC6120 10.3.3 (2)) Iq.Stanza unavailable_error = new Iq.Stanza.error(iq, new ErrorStanza.service_unavailable()) { to=iq.from }; send_iq(stream, unavailable_error); } } } private class ResponseListener { public OnResult on_result { get; private owned set; } public ResponseListener(owned OnResult on_result) { this.on_result = (owned) on_result; } } } public interface Handler : Object { public async virtual void on_iq_get(XmppStream stream, Iq.Stanza iq) { Iq.Stanza bad_request = new Iq.Stanza.error(iq, new ErrorStanza.bad_request("unexpected IQ get for this namespace")); stream.get_module(Module.IDENTITY).send_iq(stream, bad_request); } public async virtual void on_iq_set(XmppStream stream, Iq.Stanza iq) { Iq.Stanza bad_request = new Iq.Stanza.error(iq, new ErrorStanza.bad_request("unexpected IQ set for this namespace")); stream.get_module(Module.IDENTITY).send_iq(stream, bad_request); } } } dino-0.4.3/xmpp-vala/src/module/iq/stanza.vala0000644000000000000000000000231614452563620017715 0ustar rootrootusing Gee; namespace Xmpp.Iq { public class Stanza : Xmpp.Stanza { public const string TYPE_GET = "get"; public const string TYPE_RESULT = "result"; public const string TYPE_SET = "set"; private Stanza(string? id = null) { base.outgoing(new StanzaNode.build("iq")); this.id = id ?? random_uuid(); } public Stanza.get(StanzaNode stanza_node, string? id = null) { this(id); this.type_ = TYPE_GET; stanza.put_node(stanza_node); } public Stanza.result(Stanza request, StanzaNode? stanza_node = null) { this(request.id); this.to = request.from; this.type_ = TYPE_RESULT; if (stanza_node != null) { stanza.put_node(stanza_node); } } public Stanza.set(StanzaNode stanza_node, string? id = null) { this(id); this.type_ = TYPE_SET; stanza.put_node(stanza_node); } public Stanza.error(Stanza request, ErrorStanza error_stanza) { this(request.id); this.type_ = TYPE_ERROR; stanza.put_node(error_stanza.error_node); } public Stanza.from_stanza(StanzaNode stanza_node, Jid? my_jid) { base.incoming(stanza_node, my_jid); } } } dino-0.4.3/xmpp-vala/src/module/jid.vala0000644000000000000000000001574414452563620016563 0ustar rootrootnamespace Xmpp { public class Jid { public string? localpart; public string domainpart; public string? resourcepart; public Jid bare_jid { owned get { return is_bare() ? this : new Jid.intern(null, localpart, domainpart, null); } } public Jid domain_jid { owned get { return is_domain() ? this : new Jid.intern(domainpart, null, domainpart, null); } } private string jid; public Jid(string jid) throws InvalidJidError { int slash_index = jid.index_of("/"); int at_index = jid.index_of("@"); if (at_index > slash_index && slash_index != -1) at_index = -1; string resourcepart = slash_index < 0 ? null : jid.slice(slash_index + 1, jid.length); string localpart = at_index < 0 ? null : jid.slice(0, at_index); string domainpart; if (at_index < 0) { if (slash_index < 0) { domainpart = jid; } else { domainpart = jid.slice(0, slash_index); } } else { if (slash_index < 0) { domainpart = jid.slice(at_index + 1, jid.length); } else { domainpart = jid.slice(at_index + 1, slash_index); } } this.components(localpart, domainpart, resourcepart); } private Jid.intern(owned string? jid, owned string? localpart, owned string domainpart, owned string? resourcepart) { this.jid = (owned) jid; this.localpart = (owned) localpart; this.domainpart = (owned) domainpart; this.resourcepart = (owned) resourcepart; } public Jid.components(string? localpart, string domainpart, string? resourcepart) throws InvalidJidError { // TODO verify and normalize all parts if (domainpart.length == 0) throw new InvalidJidError.EMPTY_DOMAIN("Domain is empty"); if (localpart != null && localpart.length == 0) throw new InvalidJidError.EMPTY_LOCAL("Localpart is empty but non-null"); if (resourcepart != null && resourcepart.length == 0) throw new InvalidJidError.EMPTY_RESOURCE("Resource is empty but non-null"); string domain = domainpart[domainpart.length - 1] == '.' ? domainpart.substring(0, domainpart.length - 1) : domainpart; if (domain.contains("xn--")) { domain = idna_decode(domain); } this.localpart = prepare(localpart, ICU.PrepType.RFC3920_NODEPREP); this.domainpart = prepare(domain, ICU.PrepType.RFC3491_NAMEPREP); this.resourcepart = prepare(resourcepart, ICU.PrepType.RFC3920_RESOURCEPREP); idna_verify(this.domainpart); } private static string idna_decode(string src) throws InvalidJidError { ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.IDNAInfo info; char[] dest = new char[src.length * 2]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToUnicodeUTF8(src, -1, dest, out info, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status.is_failure() || info.errors > 0) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } return (string) dest; } private static void idna_verify(string src) throws InvalidJidError { ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.IDNAInfo info; char[] dest = new char[src.length * 2]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToASCII_UTF8(src, -1, dest, out info, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status.is_failure() || info.errors > 0) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } } private static string? prepare(string? src, ICU.PrepType type, bool strict = false) throws InvalidJidError { if (src == null) return src; try { ICU.ParseError error; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.PrepProfile profile = ICU.PrepProfile.openByType(type, ref status); ICU.String src16 = ICU.String.from_string(src); int32 dest16_capacity = src16.len() * 2 + 1; ICU.String dest16 = ICU.String.alloc(dest16_capacity); long dest16_length = profile.prepare(src16, src16.len(), dest16, dest16_capacity, strict ? ICU.PrepOptions.DEFAULT : ICU.PrepOptions.ALLOW_UNASSIGNED, out error, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status == ICU.ErrorCode.STRINGPREP_PROHIBITED_ERROR) { throw new InvalidJidError.INVALID_CHAR("Found prohibited character"); } else if (status != ICU.ErrorCode.ZERO_ERROR) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } else if (dest16_length < 0) { throw new InvalidJidError.UNKNOWN("Unknown error"); } return dest16.to_string(); } catch (ConvertError e) { throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)"); } } public Jid with_resource(string? resourcepart) throws InvalidJidError { return new Jid.components(localpart, domainpart, resourcepart); } public bool is_domain() { return localpart == null && resourcepart == null; } public bool is_bare() { return resourcepart == null; } public bool is_full() { return localpart != null && resourcepart != null; } public string to_string() { if (jid == null) { if (localpart != null && resourcepart != null) { jid = @"$localpart@$domainpart/$resourcepart"; } else if (localpart != null) { jid = @"$localpart@$domainpart"; } else if (resourcepart != null) { jid = @"$domainpart/$resourcepart"; } else { jid = domainpart; } } return jid; } public bool equals_bare(Jid? jid) { return jid != null && equals_bare_func(this, jid); } public bool equals(Jid? jid) { return jid != null && equals_func(this, jid); } public static new bool equals_bare_func(Jid jid1, Jid jid2) { return jid1.localpart == jid2.localpart && jid1.domainpart == jid2.domainpart; } public static bool equals_func(Jid jid1, Jid jid2) { return equals_bare_func(jid1, jid2) && jid1.resourcepart == jid2.resourcepart; } public static new uint hash_bare_func(Jid jid) { return jid.bare_jid.to_string().hash(); } public static new uint hash_func(Jid jid) { return jid.to_string().hash(); } } public errordomain InvalidJidError { EMPTY_DOMAIN, EMPTY_RESOURCE, EMPTY_LOCAL, INVALID_CHAR, UNKNOWN } } dino-0.4.3/xmpp-vala/src/module/message/0000755000000000000000000000000014452563620016561 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/message/module.vala0000644000000000000000000000433714452563620020722 0ustar rootrootusing Gee; namespace Xmpp { private const string NS_URI = "jabber:client"; public class MessageModule : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "message_module"); public StanzaListenerHolder received_pipeline = new StanzaListenerHolder(); public StanzaListenerHolder send_pipeline = new StanzaListenerHolder(); public signal void received_message(XmppStream stream, MessageStanza message); public signal void received_error(XmppStream stream, MessageStanza message, ErrorStanza error); public signal void received_message_unprocessed(XmppStream stream, MessageStanza message); public async void send_message(XmppStream stream, MessageStanza message) throws IOError { yield send_pipeline.run(stream, message); yield stream.write_async(message.stanza); } public async void received_message_stanza_async(XmppStream stream, StanzaNode node) { MessageStanza message = new MessageStanza.from_stanza(node, stream.get_flag(Bind.Flag.IDENTITY).my_jid); received_message_unprocessed(stream, message); if (message.is_error()) { ErrorStanza? error_stanza = message.get_error(); if (error_stanza == null) return; received_error(stream, message, error_stanza); } else { bool abort = yield received_pipeline.run(stream, message); if (abort) return; received_message(stream, message); } } private void received_message_stanza(XmppStream stream, StanzaNode node) { received_message_stanza_async.begin(stream, node); } public override void attach(XmppStream stream) { stream.received_message_stanza.connect(received_message_stanza); } public override void detach(XmppStream stream) { stream.received_message_stanza.disconnect(received_message_stanza); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/message/stanza.vala0000644000000000000000000000363014452563620020730 0ustar rootrootusing Gee; namespace Xmpp { public class MessageStanza : Xmpp.Stanza { public const string NODE_BODY = "body"; public const string NODE_SUBJECT = "subject"; public const string NODE_THREAD = "thread"; public const string TYPE_CHAT = "chat"; public const string TYPE_GROUPCHAT = "groupchat"; public const string TYPE_HEADLINE = "headline"; public const string TYPE_NORMAL = "normal"; public bool rerun_parsing = false; private ArrayList flags = new ArrayList(); public string body { get { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); return body_node == null ? null : body_node.get_string_content(); } set { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); if (body_node == null) { body_node = new StanzaNode.build(NODE_BODY); stanza.put_node(body_node); } body_node.sub_nodes.clear(); body_node.put_node(new StanzaNode.text(value)); } } public override string? type_ { get { return base.type_ ?? TYPE_NORMAL; } set { base.type_ = value; } } public MessageStanza(string? id = null) { base.outgoing(new StanzaNode.build("message")); stanza.set_attribute(ATTRIBUTE_ID, id ?? random_uuid()); } public MessageStanza.from_stanza(StanzaNode stanza_node, Jid my_jid) { base.incoming(stanza_node, my_jid); } public void add_flag(MessageFlag flag) { flags.add(flag); } public MessageFlag? get_flag(string ns, string id) { foreach (MessageFlag flag in flags) { if (flag.get_ns() == ns && flag.get_id() == id) return flag; } return null; } } public abstract class MessageFlag : Object { public abstract string get_ns(); public abstract string get_id(); } } dino-0.4.3/xmpp-vala/src/module/presence/0000755000000000000000000000000014452563620016741 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/presence/flag.vala0000644000000000000000000000434214452563620020522 0ustar rootrootusing Gee; namespace Xmpp.Presence { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "presence"); private HashMap> resources = new HashMap>(Jid.hash_bare_func, Jid.equals_bare_func); private HashMap presences = new HashMap(Jid.hash_func, Jid.equals_func); public Set get_available_jids() { return resources.keys; } public Gee.List? get_resources(Jid jid) { if (!resources.has_key(jid)) return null; ArrayList ret = new ArrayList(Jid.equals_func); ret.add_all(resources[jid]); return ret; } public Presence.Stanza? get_presence(Jid full_jid) { return presences[full_jid]; } public Gee.List get_presences(Jid jid) { Gee.List ret = new ArrayList(); Gee.List? jid_res = resources[jid]; if (jid_res == null) return ret; foreach (Jid full_jid in jid_res) { ret.add(presences[full_jid]); } return ret; } public void add_presence(Presence.Stanza presence) { if (!resources.has_key(presence.from)) { resources[presence.from] = new ArrayList(Jid.equals_func); } if (resources[presence.from].contains(presence.from)) { resources[presence.from].remove(presence.from); } resources[presence.from].add(presence.from); presences[presence.from] = presence; } public void remove_presence(Jid jid) { if (resources.has_key(jid)) { if (jid.is_bare()) { foreach (Jid full_jid in resources[jid]) { presences.unset(full_jid); } resources.unset(jid); } else { resources[jid].remove(jid); if (resources[jid].size == 0) { resources.unset(jid); } presences.unset(jid); } } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/presence/module.vala0000644000000000000000000001156314452563620021101 0ustar rootrootusing Gee; namespace Xmpp.Presence { private const string NS_URI = "jabber:client"; public class Module : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "presence_module"); public signal void received_presence(XmppStream stream, Presence.Stanza presence); public signal void pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence); public signal void initial_presence_sent(XmppStream stream, Presence.Stanza presence); public signal void received_available(XmppStream stream, Presence.Stanza presence); public signal void received_available_show(XmppStream stream, Jid jid, string show); public signal void received_unavailable(XmppStream stream, Presence.Stanza presence); public signal void received_subscription_request(XmppStream stream, Jid jid); public signal void received_subscription_approval(XmppStream stream, Jid jid); public signal void received_unsubscription(XmppStream stream, Jid jid); public bool available_resource = true; public void request_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_SUBSCRIBE; send_presence(stream, presence); } public void approve_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_SUBSCRIBED; send_presence(stream, presence); } public void deny_subscription(XmppStream stream, Jid bare_jid) { cancel_subscription(stream, bare_jid); } public void cancel_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBED; send_presence(stream, presence); } public void unsubscribe(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBE; send_presence(stream, presence); } public void send_presence(XmppStream stream, Presence.Stanza presence) { pre_send_presence_stanza(stream, presence); stream.write(presence.stanza); } public override void attach(XmppStream stream) { stream.add_flag(new Flag()); stream.received_presence_stanza.connect(on_received_presence_stanza); stream.stream_negotiated.connect(on_stream_negotiated); } public override void detach(XmppStream stream) { stream.received_presence_stanza.disconnect(on_received_presence_stanza); stream.stream_negotiated.disconnect(on_stream_negotiated); } private void on_received_presence_stanza(XmppStream stream, StanzaNode node) { Presence.Stanza presence = new Presence.Stanza.from_stanza(node, stream.get_flag(Bind.Flag.IDENTITY).my_jid); received_presence(stream, presence); switch (presence.type_) { case Presence.Stanza.TYPE_AVAILABLE: stream.get_flag(Flag.IDENTITY).add_presence(presence); received_available(stream, presence); received_available_show(stream, presence.from, presence.show); break; case Presence.Stanza.TYPE_UNAVAILABLE: stream.get_flag(Flag.IDENTITY).remove_presence(presence.from); received_unavailable(stream, presence); break; case Presence.Stanza.TYPE_SUBSCRIBE: received_subscription_request(stream, presence.from); break; case Presence.Stanza.TYPE_SUBSCRIBED: received_subscription_approval(stream, presence.from); break; case Presence.Stanza.TYPE_UNSUBSCRIBE: stream.get_flag(Flag.IDENTITY).remove_presence(presence.from); received_unsubscription(stream, presence.from); break; case Presence.Stanza.TYPE_UNSUBSCRIBED: break; } } private void on_stream_negotiated(XmppStream stream) { if (available_resource) { Presence.Stanza presence = new Presence.Stanza(); send_presence(stream, presence); initial_presence_sent(stream, presence); } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/presence/stanza.vala0000644000000000000000000000566414452563620021121 0ustar rootrootnamespace Xmpp.Presence { public class Stanza : Xmpp.Stanza { public const string NODE_PRIORITY = "priority"; public const string NODE_STATUS = "status"; public const string NODE_SHOW = "show"; public const string SHOW_ONLINE = "online"; public const string SHOW_AWAY = "away"; public const string SHOW_CHAT = "chat"; public const string SHOW_DND = "dnd"; public const string SHOW_XA = "xa"; public const string TYPE_AVAILABLE = "available"; public const string TYPE_PROBE = "probe"; public const string TYPE_SUBSCRIBE = "subscribe"; public const string TYPE_SUBSCRIBED = "subscribed"; public const string TYPE_UNAVAILABLE = "unavailable"; public const string TYPE_UNSUBSCRIBE = "unsubscribe"; public const string TYPE_UNSUBSCRIBED = "unsubscribed"; public int priority { get { StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); if (priority_node == null) { return 0; } else { return int.parse(priority_node.get_string_content()); } } set { StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); if (priority_node == null) { priority_node = new StanzaNode.build(NODE_PRIORITY); stanza.put_node(priority_node); } priority_node.val = value.to_string(); } } public string? status { get { StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); return status_node != null ? status_node.get_string_content() : null; } set { StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); if (status_node == null) { status_node = new StanzaNode.build(NODE_STATUS); stanza.put_node(status_node); } status_node.val = value; } } public string show { get { StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); if (show_node == null) { return SHOW_ONLINE; } return show_node.get_string_content() ?? SHOW_ONLINE; } set { if (value != SHOW_ONLINE) { StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); if (show_node == null) { show_node = new StanzaNode.build(NODE_SHOW); stanza.put_node(show_node); } show_node.val = value; } } } public override string? type_ { get { return base.type_ ?? TYPE_AVAILABLE; } set { base.type_ = value; } } public Stanza(string? id = null) { stanza = new StanzaNode.build("presence"); this.id = id ?? random_uuid(); } public Stanza.from_stanza(StanzaNode stanza_node, Jid my_jid) { base.incoming(stanza_node, my_jid); } } } dino-0.4.3/xmpp-vala/src/module/roster/0000755000000000000000000000000014452563620016453 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/roster/flag.vala0000644000000000000000000000110414452563620020225 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class Flag : XmppStreamFlag { public const string ID = "roster"; public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, ID); public HashMap roster_items = new HashMap(); public string? iq_id; public Collection get_roster() { return roster_items.values; } public Item? get_item(Jid jid) { return roster_items[jid]; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } }dino-0.4.3/xmpp-vala/src/module/roster/item.vala0000644000000000000000000000271714452563620020265 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class Item { public const string NODE_JID = "jid"; public const string NODE_NAME = "name"; public const string NODE_SUBSCRIPTION = "subscription"; public const string SUBSCRIPTION_NONE = "none"; public const string SUBSCRIPTION_TO = "to"; public const string SUBSCRIPTION_FROM = "from"; public const string SUBSCRIPTION_BOTH = "both"; public const string SUBSCRIPTION_REMOVE = "remove"; public StanzaNode stanza_node; private Jid jid_; public Jid? jid { get { try { return jid_ ?? (jid_ = new Jid(stanza_node.get_attribute(NODE_JID))); } catch (InvalidJidError e) { warning("Ignoring invalid Jid in roster entry: %s", e.message); return null; } } set { stanza_node.set_attribute(NODE_JID, value.to_string()); } } public string? name { get { return stanza_node.get_attribute(NODE_NAME); } set { if (value != null) stanza_node.set_attribute(NODE_NAME, value); } } public string? subscription { get { return stanza_node.get_attribute(NODE_SUBSCRIPTION); } set { if (value != null) stanza_node.set_attribute(NODE_SUBSCRIPTION, value); } } public Item() { stanza_node = new StanzaNode.build("item", NS_URI); } public Item.from_stanza_node(StanzaNode stanza_node) { this.stanza_node = stanza_node; } } } dino-0.4.3/xmpp-vala/src/module/roster/module.vala0000644000000000000000000001124214452563620020605 0ustar rootrootusing Gee; namespace Xmpp.Roster { private const string NS_URI = "jabber:iq:roster"; public class Module : XmppStreamModule, Iq.Handler { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "roster_module"); public signal void received_roster(XmppStream stream, Collection roster, Iq.Stanza stanza); public signal void pre_get_roster(XmppStream stream, Iq.Stanza iq); public signal void item_removed(XmppStream stream, Item item, Iq.Stanza iq); public signal void item_updated(XmppStream stream, Item item, Iq.Stanza iq); public signal void mutual_subscription(XmppStream stream, Jid jid); public bool interested_resource = true; public void add_jid(XmppStream stream, Jid jid, string? handle = null) { Item roster_item = new Item(); roster_item.jid = jid; if (handle != null) { roster_item.name = handle; } roster_set(stream, roster_item); } public void remove_jid(XmppStream stream, Jid jid) { Item roster_item = new Item(); roster_item.jid = jid; roster_item.subscription = Item.SUBSCRIPTION_REMOVE; roster_set(stream, roster_item); } /** * Set a handle for a jid * @param handle Handle to be set. If null, any handle will be removed. */ public void set_jid_handle(XmppStream stream, Jid jid, string? handle) { Flag flag = stream.get_flag(Flag.IDENTITY); Item item = flag.get_item(jid) ?? new Item() { jid=jid }; item.name = handle != null ? handle : ""; roster_set(stream, item); } public async void on_iq_set(XmppStream stream, Iq.Stanza iq) { StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); if (query_node == null) return; if (!iq.from.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid)) { warning("Received alleged roster push from %s, ignoring", iq.from.to_string()); return; } Flag flag = stream.get_flag(Flag.IDENTITY); Item item = new Item.from_stanza_node(query_node.get_subnode("item", NS_URI)); switch (item.subscription) { case Item.SUBSCRIPTION_REMOVE: flag.roster_items.unset(item.jid); item_removed(stream, item, iq); break; default: bool is_new = false; Item old = flag.get_item(item.jid); is_new = item.subscription == Item.SUBSCRIPTION_BOTH && (old == null || old.subscription == Item.SUBSCRIPTION_BOTH); flag.roster_items[item.jid] = item; item_updated(stream, item, iq); if(is_new) mutual_subscription(stream, item.jid); break; } } public override void attach(XmppStream stream) { stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI, this); stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.connect(roster_get); stream.add_flag(new Flag()); } public override void detach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.disconnect(roster_get); } internal override string get_ns() { return NS_URI; } internal override string get_id() { return IDENTITY.id; } private void roster_get(XmppStream stream) { stream.get_flag(Flag.IDENTITY).iq_id = random_uuid(); StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); Iq.Stanza iq = new Iq.Stanza.get(query_node, stream.get_flag(Flag.IDENTITY).iq_id); pre_get_roster(stream, iq); stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, on_roster_get_received); } private static void on_roster_get_received(XmppStream stream, Iq.Stanza iq) { Flag flag = stream.get_flag(Flag.IDENTITY); if (iq.id == flag.iq_id) { StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); if (query_node != null) { foreach (StanzaNode item_node in query_node.sub_nodes) { Item item = new Item.from_stanza_node(item_node); flag.roster_items[item.jid] = item; } } stream.get_module(Module.IDENTITY).received_roster(stream, flag.roster_items.values, iq); } } private void roster_set(XmppStream stream, Item roster_item) { StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns() .put_node(roster_item.stanza_node); Iq.Stanza iq = new Iq.Stanza.set(query_node); stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq); } } } dino-0.4.3/xmpp-vala/src/module/roster/versioning_module.vala0000644000000000000000000000544214452563620023055 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class VersioningModule : XmppStreamModule { private const string NS_URI_FEATURE = "urn:xmpp:features:rosterver"; public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "roster_versioning"); private Storage storage; public VersioningModule(Storage storage) { this.storage = storage; } public override void attach(XmppStream stream) { stream.get_module(Module.IDENTITY).pre_get_roster.connect(on_pre_get_roster); stream.get_module(Module.IDENTITY).received_roster.connect(on_received_roster); stream.get_module(Module.IDENTITY).item_updated.connect(on_item_updated); stream.get_module(Module.IDENTITY).item_removed.connect(on_item_removed); } public override void detach(XmppStream stream) { stream.get_module(Module.IDENTITY).pre_get_roster.disconnect(on_pre_get_roster); } internal override string get_ns() { return NS_URI; } internal override string get_id() { return IDENTITY.id; } private void on_pre_get_roster(XmppStream stream, Iq.Stanza iq) { StanzaNode? ver_feature = stream.features.get_subnode("ver", NS_URI_FEATURE); if (ver_feature != null) { iq.stanza.get_subnode("query", NS_URI).set_attribute("ver", storage.get_roster_version() ?? ""); } } private void on_received_roster(XmppStream stream, Collection roster, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); if (iq.stanza.get_subnode("query", NS_URI) != null) { storage.set_roster(roster); } else { Flag flag = stream.get_flag(Flag.IDENTITY); foreach (Item item in storage.get_roster()) { flag.roster_items[item.jid] = item; } } } private void on_item_updated(XmppStream stream, Item item, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); storage.set_item(item); } private void on_item_removed(XmppStream stream, Item item, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); storage.remove_item(item); } } public interface Storage : Object { public abstract string? get_roster_version(); public abstract Collection get_roster(); public abstract void set_roster_version(string version); public abstract void set_roster(Collection items); public abstract void set_item(Roster.Item item); public abstract void remove_item(Roster.Item item); } } dino-0.4.3/xmpp-vala/src/module/sasl.vala0000644000000000000000000002564114452563620016754 0ustar rootrootnamespace Xmpp.Sasl { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-sasl"; public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "sasl"); public string mechanism; public string name; public string password; public string client_nonce; public uint8[] server_signature; public bool finished = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } namespace Mechanism { public const string PLAIN = "PLAIN"; public const string SCRAM_SHA_1 = "SCRAM-SHA-1"; public const string SCRAM_SHA_1_PLUS = "SCRAM-SHA-1-PLUS"; } public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "sasl"); public string name { get; set; } public string password { get; set; } public bool use_full_name = false; public signal void received_auth_failure(XmppStream stream, StanzaNode node); public Module(string name, string password) { this.name = name; this.password = password; } public override void attach(XmppStream stream) { stream.received_features_node.connect(this.received_features_node); stream.received_nonza.connect(this.received_nonza); } public override void detach(XmppStream stream) { stream.received_features_node.disconnect(this.received_features_node); stream.received_nonza.disconnect(this.received_nonza); } private static size_t SHA1_SIZE = 20; private static uint8[] sha1(uint8[] data) { Checksum checksum = new Checksum(ChecksumType.SHA1); checksum.update(data, data.length); uint8[] res = new uint8[SHA1_SIZE]; checksum.get_digest(res, ref SHA1_SIZE); return res; } private static uint8[] hmac_sha1(uint8[] key, uint8[] data) { Hmac hmac = new Hmac(ChecksumType.SHA1, key); hmac.update(data); uint8[] res = new uint8[SHA1_SIZE]; hmac.get_digest(res, ref SHA1_SIZE); return res; } private static uint8[] pbkdf2_sha1(string password, uint8[] salt, uint iterations) { uint8[] res = new uint8[SHA1_SIZE]; uint8[] last = new uint8[salt.length + 4]; for(int i = 0; i < salt.length; i++) { last[i] = salt[i]; } last[salt.length + 3] = 1; for(int i = 0; i < iterations; i++) { last = hmac_sha1((uint8[]) password.to_utf8(), last); xor_inplace(res, last); } return res; } private static void xor_inplace(uint8[] mix, uint8[] a2) { for(int i = 0; i < mix.length; i++) { mix[i] = mix[i] ^ a2[i]; } } private static uint8[] xor(uint8[] a1, uint8[] a2) { uint8[] mix = new uint8[a1.length]; for(int i = 0; i < a1.length; i++) { mix[i] = a1[i] ^ a2[i]; } return mix; } public void received_nonza(XmppStream stream, StanzaNode node) { if (node.ns_uri == NS_URI) { if (node.name == "success") { Flag flag = stream.get_flag(Flag.IDENTITY); if (flag.mechanism == Mechanism.SCRAM_SHA_1) { string confirm = (string) Base64.decode(node.get_string_content()); uint8[] server_signature = null; foreach(string c in confirm.split(",")) { string[] split = c.split("=", 2); if (split.length != 2) continue; switch(split[0]) { case "v": server_signature = Base64.decode(split[1]); break; } } if (server_signature == null) return; if (server_signature.length != flag.server_signature.length) return; for(int i = 0; i < server_signature.length; i++) { if (server_signature[i] != flag.server_signature[i]) return; } } stream.require_setup(); flag.password = null; // Remove password from memory flag.finished = true; } else if (node.name == "failure") { stream.remove_flag(stream.get_flag(Flag.IDENTITY)); received_auth_failure(stream, node); } else if (node.name == "challenge" && stream.has_flag(Flag.IDENTITY)) { Flag flag = stream.get_flag(Flag.IDENTITY); if (flag.mechanism == Mechanism.SCRAM_SHA_1) { string challenge = (string) Base64.decode(node.get_string_content()); string? server_nonce = null; uint8[] salt = null; uint iterations = 0; foreach(string c in challenge.split(",")) { string[] split = c.split("=", 2); if (split.length != 2) continue; switch(split[0]) { case "r": server_nonce = split[1]; break; case "s": salt = Base64.decode(split[1]); break; case "i": iterations = int.parse(split[1]); break; } } if (server_nonce == null || salt == null || iterations == 0) return; if (!server_nonce.has_prefix(flag.client_nonce)) return; string client_final_message_bare = @"c=biws,r=$server_nonce"; uint8[] salted_password = pbkdf2_sha1(flag.password, salt, iterations); uint8[] client_key = hmac_sha1(salted_password, (uint8[]) "Client Key".to_utf8()); uint8[] stored_key = sha1(client_key); string auth_message = @"n=$(flag.name),r=$(flag.client_nonce),$challenge,$client_final_message_bare"; uint8[] client_signature = hmac_sha1(stored_key, (uint8[]) auth_message.to_utf8()); uint8[] client_proof = xor(client_key, client_signature); uint8[] server_key = hmac_sha1(salted_password, (uint8[]) "Server Key".to_utf8()); flag.server_signature = hmac_sha1(server_key, (uint8[]) auth_message.to_utf8()); string client_final_message = @"$client_final_message_bare,p=$(Base64.encode(client_proof))"; stream.write(new StanzaNode.build("response", NS_URI).add_self_xmlns() .put_node(new StanzaNode.text(Base64.encode((uchar[]) (client_final_message).to_utf8())))); } } } } public void received_features_node(XmppStream stream) { if (stream.has_flag(Flag.IDENTITY)) return; if (stream.is_setup_needed()) return; var mechanisms = stream.features.get_subnode("mechanisms", NS_URI); string[] supported_mechanisms = {}; foreach (var mechanism in mechanisms.sub_nodes) { if (mechanism.name != "mechanism" || mechanism.ns_uri != NS_URI) continue; supported_mechanisms += mechanism.get_string_content(); } if (!name.contains("@")) { name = "%s@%s".printf(name, stream.remote_name.to_string()); } if (!use_full_name && name.contains("@")) { var split = name.split("@"); if (split[1] == stream.remote_name.to_string()) { name = split[0]; } else { use_full_name = true; } } string name = this.name; if (!use_full_name && name.contains("@")) { var split = name.split("@"); if (split[1] == stream.remote_name.to_string()) { name = split[0]; } } if (Mechanism.SCRAM_SHA_1 in supported_mechanisms) { string normalized_password = password.normalize(-1, NormalizeMode.NFKC); string client_nonce = Random.next_int().to_string("%.8x") + Random.next_int().to_string("%.8x") + Random.next_int().to_string("%.8x"); string initial_message = @"n=$name,r=$client_nonce"; stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() .put_attribute("mechanism", Mechanism.SCRAM_SHA_1) .put_node(new StanzaNode.text(Base64.encode((uchar[]) ("n,,"+initial_message).to_utf8())))); var flag = new Flag(); flag.mechanism = Mechanism.SCRAM_SHA_1; flag.name = name; flag.password = normalized_password; flag.client_nonce = client_nonce; stream.add_flag(flag); } else if (Mechanism.PLAIN in supported_mechanisms) { stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() .put_attribute("mechanism", Mechanism.PLAIN) .put_node(new StanzaNode.text(Base64.encode(get_plain_bytes(name, password))))); var flag = new Flag(); flag.mechanism = Mechanism.PLAIN; flag.name = name; stream.add_flag(flag); } else { stderr.printf("No supported mechanism provided by server at %s\n", stream.remote_name.to_string()); return; } } private static uchar[] get_plain_bytes(string name_s, string password_s) { var name = name_s.to_utf8(); var password = password_s.to_utf8(); uchar[] res = new uchar[name.length + password.length + 2]; res[0] = 0; res[name.length + 1] = 0; for(int i = 0; i < name.length; i++) { res[i + 1] = (uchar) name[i]; } for(int i = 0; i < password.length; i++) { res[i + name.length + 2] = (uchar) password[i]; } return res; } public override bool mandatory_outstanding(XmppStream stream) { return !stream.has_flag(Flag.IDENTITY) || !stream.get_flag(Flag.IDENTITY).finished; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/session.vala0000644000000000000000000000354514452563620017474 0ustar rootroot/* Legacy. RFC 3921 3*/ namespace Xmpp.Session { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-session"; public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "session"); public override void attach(XmppStream stream) { stream.get_module(Bind.Module.IDENTITY).bound_to_resource.connect(on_bound_resource); } public override void detach(XmppStream stream) { stream.get_module(Bind.Module.IDENTITY).bound_to_resource.disconnect(on_bound_resource); } public override bool mandatory_outstanding(XmppStream stream) { return false; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private async void on_bound_resource(XmppStream stream, Jid my_jid) { StanzaNode? session_node = stream.features.get_subnode("session", NS_URI); if (session_node != null && session_node.get_subnode("optional", NS_URI) == null) { stream.add_flag(new Flag()); Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("session", NS_URI).add_self_xmlns()) { to=stream.remote_name }; Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); if (!result_iq.is_error()) { stream.get_flag(Flag.IDENTITY).finished = true; } } } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "session"); public bool finished = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/stanza.vala0000644000000000000000000000477714452563620017321 0ustar rootrootnamespace Xmpp { public class Stanza : Object { public const string ATTRIBUTE_FROM = "from"; public const string ATTRIBUTE_ID = "id"; public const string ATTRIBUTE_TO = "to"; public const string ATTRIBUTE_TYPE = "type"; public const string TYPE_ERROR = "error"; private Jid? my_jid; private Jid? from_; private Jid? to_; public virtual Jid? from { owned get { string? from_attribute = stanza.get_attribute(ATTRIBUTE_FROM); // "when a client receives a stanza that does not include a 'from' attribute, it MUST assume that the stanza // is from the user's account on the server." (RFC6120 8.1.2.1) if (from_attribute != null) { try { return from_ = new Jid(from_attribute); } catch (InvalidJidError e) { warning("Ignoring invalid from Jid: %s", e.message); } } if (my_jid != null) { return my_jid.bare_jid; } return null; } set { stanza.set_attribute(ATTRIBUTE_FROM, value.to_string()); } } public virtual string? id { get { return stanza.get_attribute(ATTRIBUTE_ID); } set { stanza.set_attribute(ATTRIBUTE_ID, value); } } public virtual Jid? to { owned get { string? to_attribute = stanza.get_attribute(ATTRIBUTE_TO); // "if the stanza does not include a 'to' address then the client MUST treat it as if the 'to' address were // included with a value of the client's full JID." (RFC6120 8.1.1.1) try { return to_attribute == null ? my_jid : to_ = new Jid(to_attribute); } catch (InvalidJidError e) { warning("Ignoring invalid to Jid: %s", e.message); } return my_jid; } set { stanza.set_attribute(ATTRIBUTE_TO, value.to_string()); } } public virtual string? type_ { get { return stanza.get_attribute(ATTRIBUTE_TYPE); } set { stanza.set_attribute(ATTRIBUTE_TYPE, value); } } public StanzaNode stanza; public Stanza.incoming(StanzaNode stanza, Jid? my_jid) { this.stanza = stanza; this.my_jid = my_jid; } public Stanza.outgoing(StanzaNode stanza) { this.stanza = stanza; } public bool is_error() { return type_ == TYPE_ERROR; } public ErrorStanza? get_error() { return ErrorStanza.from_stanza(this.stanza); } } }dino-0.4.3/xmpp-vala/src/module/stanza_error.vala0000644000000000000000000001203214452563620020511 0ustar rootrootusing Gee; namespace Xmpp { private const string ERROR_NS_URI = "urn:ietf:params:xml:ns:xmpp-stanzas"; public class ErrorStanza { public const string CONDITION_BAD_REQUEST = "bad-request"; public const string CONDITION_CONFLICT = "conflict"; public const string CONDITION_FEATURE_NOT_IMPLEMENTED = "feature-not-implemented"; public const string CONDITION_FORBIDDEN = "forbidden"; public const string CONDITION_GONE = "gone"; public const string CONDITION_INTERNAL_SERVER_ERROR = "internal-server-error"; public const string CONDITION_ITEM_NOT_FOUND = "item-not-found"; public const string CONDITION_JID_MALFORMED = "jid-malformed"; public const string CONDITION_NOT_ACCEPTABLE = "not-acceptable"; public const string CONDITION_NOT_ALLOWED = "not-allowed"; public const string CONDITION_NOT_AUTHORIZED = "not-authorized"; public const string CONDITION_POLICY_VIOLATION = "policy-violation"; public const string CONDITION_RECIPIENT_UNAVAILABLE = "recipient-unavailable"; public const string CONDITION_REDIRECT = "redirect"; public const string CONDITION_REGISTRATION_REQUIRED = "registration-required"; public const string CONDITION_REMOTE_SERVER_NOT_FOUND = "remote-server-not-found"; public const string CONDITION_REMOTE_SERVER_TIMEOUT = "remote-server-timeout"; public const string CONDITION_RESOURCE_CONSTRAINT = "resource-constraint"; public const string CONDITION_SERVICE_UNAVAILABLE = "service-unavailable"; public const string CONDITION_SUBSCRIPTION_REQUIRED = "subscription-required"; public const string CONDITION_UNDEFINED_CONDITION = "undefined-condition"; public const string CONDITION_UNEXPECTED_REQUEST = "unexpected-request"; public const string TYPE_AUTH = "auth"; public const string TYPE_CANCEL = "cancel"; public const string TYPE_CONTINUE = "continue"; public const string TYPE_MODIFY = "modify"; public const string TYPE_WAIT = "wait"; public string? by { get { return error_node.get_attribute("by"); } } public string? text { get { return error_node.get_deep_string_content(ERROR_NS_URI + ":text"); } } public string condition { get { Gee.List subnodes = error_node.sub_nodes; foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns if (subnode.ns_uri == ERROR_NS_URI) { return subnode.name; } } return CONDITION_UNDEFINED_CONDITION; // TODO hm! } } public string type_ { get { return error_node.get_attribute("type"); } } public StanzaNode error_node; public static ErrorStanza? from_stanza(StanzaNode stanza) { var error_stanza = new ErrorStanza(); error_stanza.error_node = stanza.get_subnode("error"); if (error_stanza.error_node == null) return null; return error_stanza; } public ErrorStanza.build(string type, string condition, string? human_readable, StanzaNode? application_condition) { error_node = new StanzaNode.build("error") .put_attribute("type", type) .put_node(new StanzaNode.build(condition, ERROR_NS_URI).add_self_xmlns()); if (application_condition != null) { error_node.put_node(application_condition); } if (human_readable != null) { error_node.put_node(new StanzaNode.build("text", ERROR_NS_URI) .add_self_xmlns() .put_attribute("xml:lang", "en") .put_node(new StanzaNode.text(human_readable)) ); } } public ErrorStanza.bad_request(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_BAD_REQUEST, human_readable, null); } public ErrorStanza.feature_not_implemented(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_FEATURE_NOT_IMPLEMENTED, human_readable, null); } public ErrorStanza.item_not_found(StanzaNode? application_condition = null) { this.build(TYPE_CANCEL, CONDITION_ITEM_NOT_FOUND, null, application_condition); } public ErrorStanza.not_acceptable(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_NOT_ACCEPTABLE, human_readable, null); } public ErrorStanza.not_allowed(string? human_readable = null) { this.build(TYPE_CANCEL, CONDITION_NOT_ALLOWED, human_readable, null); } public ErrorStanza.resource_constraint(string? human_readable = null) { this.build(TYPE_WAIT, CONDITION_RESOURCE_CONSTRAINT, human_readable, null); } public ErrorStanza.service_unavailable() { this.build(TYPE_CANCEL, CONDITION_SERVICE_UNAVAILABLE, null, null); } } } dino-0.4.3/xmpp-vala/src/module/stream_error.vala0000644000000000000000000001013214452563620020503 0ustar rootrootusing Gee; namespace Xmpp.StreamError { private const string NS_URI = "jabber:client"; private const string NS_ERROR = "urn:ietf:params:xml:ns:xmpp-streams"; public class Module : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "stream_error_module"); public override void attach(XmppStream stream) { stream.received_nonza.connect(on_received_nonstanza); } public override void detach(XmppStream stream) { stream.received_nonza.disconnect(on_received_nonstanza); } public static void require(XmppStream stream) { if (stream.get_module(IDENTITY) == null) stream.add_module(new Module()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private void on_received_nonstanza(XmppStream stream, StanzaNode node) { if (node.name == "error" && node.ns_uri == "http://etherx.jabber.org/streams") { stream.add_flag(generate_error_flag(node)); } } private Flag generate_error_flag(StanzaNode node) { string? subnode_name = null; Gee.List subnodes = node.sub_nodes; foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") { subnode_name = subnode.name; } } Flag flag = new StreamError.Flag(); flag.error_type = subnode_name; switch (subnode_name) { case "bad-format": case "conflict": case "connection-timeout": case "bad-namespace-prefix": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "host-gone": case "host-unknown": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "improper-addressing": case "internal-server-error": case "invalid-from": case "invalid-namespace": case "invalid-xml": case "not-authorized": case "not-well-formed": case "policy-violation": case "remote-connection-failed": case "reset": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "resource-constraint": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "restricted-xml": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "see-other-host": case "system-shutdown": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "undefined-condition": case "unsupported-encoding": case "unsupported-feature": case "unsupported-stanza-type": case "unsupported-version": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; } if (subnode_name == "conflict") flag.resource_rejected = true; return flag; } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "stream_error"); public enum Reconnect { UNKNOWN, NOW, LATER, NEVER } public string? error_type; public Reconnect reconnection_recomendation = Reconnect.UNKNOWN; public bool resource_rejected = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/tls.vala0000644000000000000000000001051614452563620016607 0ustar rootrootnamespace Xmpp.Tls { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-tls"; public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "tls_module"); public signal void invalid_certificate(TlsCertificate peer_cert, TlsCertificateFlags errors); public bool require { get; set; default = true; } public bool server_supports_tls = false; public bool server_requires_tls = false; public SocketConnectable? identity = null; public override void attach(XmppStream stream) { stream.received_features_node.connect(this.received_features_node); stream.received_nonza.connect(this.received_nonza); } public override void detach(XmppStream stream) { stream.received_features_node.disconnect(this.received_features_node); stream.received_nonza.disconnect(this.received_nonza); } private void received_nonza(XmppStream stream, StanzaNode node) { if (node.ns_uri == NS_URI && node.name == "proceed") { try { StartTlsXmppStream? tls_xmpp_stream = stream as StartTlsXmppStream; var io_stream = tls_xmpp_stream.get_stream(); if (io_stream == null) return; var conn = TlsClientConnection.new(io_stream, identity); tls_xmpp_stream.reset_stream(conn); conn.accept_certificate.connect(on_invalid_certificate); var flag = stream.get_flag(Flag.IDENTITY); flag.peer_certificate = conn.get_peer_certificate(); flag.finished = true; } catch (Error e) { stderr.printf("Failed to start TLS: %s\n", e.message); } } } private void received_features_node(XmppStream stream) { if (stream.has_flag(Flag.IDENTITY)) return; if (stream.is_setup_needed()) return; var starttls = stream.features.get_subnode("starttls", NS_URI); if (starttls != null) { server_supports_tls = true; if (starttls.get_subnode("required") != null || stream.features.get_all_subnodes().size == 1) { server_requires_tls = true; } if (server_requires_tls || require) { stream.write(new StanzaNode.build("starttls", NS_URI).add_self_xmlns()); } if (identity == null) { identity = new NetworkService("xmpp-client", "tcp", stream.remote_name.to_string()); } stream.add_flag(new Flag()); } } public bool on_invalid_certificate(TlsCertificate peer_cert, TlsCertificateFlags errors) { string error_str = ""; foreach (var f in new TlsCertificateFlags[]{TlsCertificateFlags.UNKNOWN_CA, TlsCertificateFlags.BAD_IDENTITY, TlsCertificateFlags.NOT_ACTIVATED, TlsCertificateFlags.EXPIRED, TlsCertificateFlags.REVOKED, TlsCertificateFlags.INSECURE, TlsCertificateFlags.GENERIC_ERROR, TlsCertificateFlags.VALIDATE_ALL}) { if (f in errors) { error_str += @"$(f), "; } } warning(@"Tls Certificate Errors: $(error_str)"); invalid_certificate(peer_cert, errors); return false; } public override bool mandatory_outstanding(XmppStream stream) { return require && (!stream.has_flag(Flag.IDENTITY) || !stream.get_flag(Flag.IDENTITY).finished); } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "tls"); public TlsCertificate? peer_certificate; public bool finished { get; set; default=false; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.4.3/xmpp-vala/src/module/util.vala0000644000000000000000000000523714452563620016766 0ustar rootrootusing Gee; namespace Xmpp { public string random_uuid() { uint32 b1 = Random.next_int(); uint16 b2 = (uint16)Random.next_int(); uint16 b3 = (uint16)(Random.next_int() | 0x4000u) & ~0xb000u; uint16 b4 = (uint16)(Random.next_int() | 0x8000u) & ~0x4000u; uint16 b5_1 = (uint16)Random.next_int(); uint32 b5_2 = Random.next_int(); return "%08x-%04x-%04x-%04x-%04x%08x".printf(b1, b2, b3, b4, b5_1, b5_2); } public abstract class StanzaListener : OrderedListener { public abstract async bool run(XmppStream stream, T stanza); } public class StanzaListenerHolder : ListenerHolder { public async bool run(XmppStream stream, T stanza) { // listeners can change e.g. when switching to another stream ArrayList listeners_copy = new ArrayList(); listeners_copy.add_all(listeners); foreach (OrderedListener ol in listeners_copy) { StanzaListener l = ol as StanzaListener; bool stop = yield l.run(stream, stanza); if (stop) return true; } return false; } } public abstract class OrderedListener : Object { public abstract string action_group { get; } public abstract string[] after_actions { get; } } public abstract class ListenerHolder : Object { protected ArrayList listeners = new ArrayList(); public new void connect(OrderedListener listener) { listeners.add(listener); resort_list(); } public new void disconnect(OrderedListener listener) { listeners.remove(listener); resort_list(); } private bool set_contains_action(Gee.List s, string[] actions) { foreach(OrderedListener l in s) { if (l.action_group in actions) { return true; } } return false; } private void resort_list() { ArrayList new_list = new ArrayList(); ArrayList remaining = new ArrayList(); remaining.add_all(listeners); while (remaining.size > 0) { bool changed = false; Gee.Iterator iter = remaining.iterator(); while (iter.has_next()) { iter.next(); OrderedListener l = iter.get(); if (!set_contains_action(remaining, l.after_actions)) { new_list.add(l); iter.remove(); changed = true; } } if (!changed) error("Can't sort listeners"); } listeners = new_list; } } } dino-0.4.3/xmpp-vala/src/module/xep/0000755000000000000000000000000014452563620015731 5ustar rootrootdino-0.4.3/xmpp-vala/src/module/xep/0004_data_forms.vala0000644000000000000000000001734314452563620021370 0ustar rootrootusing Gee; namespace Xmpp.Xep.DataForms { public const string NS_URI = "jabber:x:data"; public class DataForm { public StanzaNode stanza_node { get; set; } public Gee.List fields = new ArrayList(); public string? form_type = null; public string? instructions = null; public string? title = null; public StanzaNode get_submit_node() { stanza_node.set_attribute("type", "submit"); return stanza_node; } public enum Type { BOOLEAN, FIXED, HIDDEN, JID_MULTI, LIST_SINGLE, LIST_MULTI, TEXT_PRIVATE, TEXT_SINGLE, } public class Option { public string label { get; set; } public string value { get; set; } public Option(string label, string value) { this.label = label; this.value = value; } } public class Field { public StanzaNode node { get; set; } public string? label { get { return node.get_attribute("label", NS_URI); } set { node.set_attribute("label", value); } } public virtual Type? type_ { get; internal set; default=null; } public string? var { get { return node.get_attribute("var", NS_URI); } set { node.set_attribute("var", value); } } public Field() { this.node = new StanzaNode.build("field", NS_URI); } public Field.from_node(StanzaNode node) { this.node = node; } internal Gee.List get_values() { Gee.List ret = new ArrayList(); Gee.List value_nodes = node.get_subnodes("value", NS_URI); foreach (StanzaNode node in value_nodes) { ret.add(node.get_string_content()); } return ret; } public string get_value_string() { Gee.List values = get_values(); return values.size > 0 ? values[0] : ""; } public void set_value_string(string val) { StanzaNode? value_node = node.get_subnode("value", NS_URI); if (value_node == null) { value_node = new StanzaNode.build("value", NS_URI); node.put_node(value_node); } value_node.sub_nodes.clear(); value_node.put_node(new StanzaNode.text(val)); } internal void add_value_string(string val) { StanzaNode node = new StanzaNode.build("value"); node.put_node(new StanzaNode.text(val)); } internal Gee.List