dino-0.1.0/ 0000755 0000000 0000000 00000000000 13614354364 011150 5 ustar root root dino-0.1.0/.gitmodules 0000644 0000000 0000000 00000000253 13614354364 013325 0 ustar root root [submodule "libsignal-protocol-c"]
path = plugins/signal-protocol/libsignal-protocol-c
url = https://github.com/WhisperSystems/libsignal-protocol-c.git
branch = v2.3.2
dino-0.1.0/CMakeLists.txt 0000644 0000000 0000000 00000021531 13614354364 013712 0 ustar root root cmake_minimum_required(VERSION 3.3)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
include(ComputeVersion)
if (NOT VERSION_FOUND)
project(Dino LANGUAGES C)
elseif (VERSION_IS_RELEASE)
project(Dino VERSION ${VERSION_FULL} LANGUAGES C)
else ()
project(Dino LANGUAGES C)
set(PROJECT_VERSION ${VERSION_FULL})
endif ()
# Prepare Plugins
set(DEFAULT_PLUGINS omemo;openpgp;http-files)
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)
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)
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(GTK3_GLOBAL_VERSION 3.22)
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") # See Dino issue #646
set(DISABLE_FAST_VAPI yes)
endif ()
include(${VALA_USE_FILE})
include(MultiFind)
include(GlibCompileResourcesSupport)
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_GLOBAL_VERSION}")
add_subdirectory(qlite)
add_subdirectory(xmpp-vala)
add_subdirectory(libdino)
add_subdirectory(main)
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.1.0/LICENSE 0000644 0000000 0000000 00000104512 13614354364 012160 0 ustar root root 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.1.0/README.md 0000644 0000000 0000000 00000003171 13614354364 012431 0 ustar root root 
=======

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 conference room at `chat@dino.im`.
- The [wiki](https://github.com/dino/dino/wiki) provides additional information.
Contribute
----------
- Pull requests are welcome. Please discuss bigger changes in the conference room first.
- Look at [how to debug](https://github.com/dino/dino/wiki/Debugging) Dino before you report a bug.
- Help [translating](https://hosted.weblate.org/projects/dino/) Dino into your language.
License
-------
Dino - Modern Jabber/XMPP Client using GTK+/Vala
Copyright (C) 2016-2020 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.1.0/VERSION 0000644 0000000 0000000 00000000016 13614354364 012215 0 ustar root root RELEASE 0.1.0
dino-0.1.0/cmake/ 0000755 0000000 0000000 00000000000 13614354364 012230 5 ustar root root dino-0.1.0/cmake/BuildTargetScript.cmake 0000644 0000000 0000000 00000004443 13614354364 016632 0 ustar root root # 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.1.0/cmake/CompileGResources.cmake 0000644 0000000 0000000 00000024044 13614354364 016630 0 ustar root root include(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.1.0/cmake/ComputeVersion.cmake 0000644 0000000 0000000 00000007537 13614354364 016230 0 ustar root root include(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.1.0/cmake/FindATK.cmake 0000644 0000000 0000000 00000002670 13614354364 014457 0 ustar root root include(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.1.0/cmake/FindCairo.cmake 0000644 0000000 0000000 00000002743 13614354364 015076 0 ustar root root include(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.1.0/cmake/FindCanberra.cmake 0000644 0000000 0000000 00000000423 13614354364 015547 0 ustar root root include(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.1.0/cmake/FindGCrypt.cmake 0000644 0000000 0000000 00000000466 13614354364 015251 0 ustar root root include(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.1.0/cmake/FindGDK3.cmake 0000644 0000000 0000000 00000003334 13614354364 014526 0 ustar root root include(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.1.0/cmake/FindGDKPixbuf2.cmake 0000644 0000000 0000000 00000001704 13614354364 015702 0 ustar root root include(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.1.0/cmake/FindGIO.cmake 0000644 0000000 0000000 00000000751 13614354364 014454 0 ustar root root include(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)
# TODO
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.1.0/cmake/FindGLib.cmake 0000644 0000000 0000000 00000003057 13614354364 014655 0 ustar root root include(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.1.0/cmake/FindGModule.cmake 0000644 0000000 0000000 00000001012 13614354364 015361 0 ustar root root include(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.1.0/cmake/FindGObject.cmake 0000644 0000000 0000000 00000001022 13614354364 015343 0 ustar root root include(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.1.0/cmake/FindGPGME.cmake 0000644 0000000 0000000 00000000452 13614354364 014673 0 ustar root root include(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.1.0/cmake/FindGTK3.cmake 0000644 0000000 0000000 00000002764 13614354364 014554 0 ustar root root include(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.1.0/cmake/FindGee.cmake 0000644 0000000 0000000 00000000532 13614354364 014533 0 ustar root root include(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.1.0/cmake/FindGettext.cmake 0000644 0000000 0000000 00000001605 13614354364 015461 0 ustar root root find_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.1.0/cmake/FindICU.cmake 0000644 0000000 0000000 00000000450 13614354364 014452 0 ustar root root include(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.1.0/cmake/FindPango.cmake 0000644 0000000 0000000 00000003061 13614354364 015077 0 ustar root root include(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.1.0/cmake/FindQrencode.cmake 0000644 0000000 0000000 00000000464 13614354364 015577 0 ustar root root include(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.1.0/cmake/FindSQLite3.cmake 0000644 0000000 0000000 00000001355 13614354364 015263 0 ustar root root include(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.1.0/cmake/FindSignalProtocol.cmake 0000644 0000000 0000000 00000000554 13614354364 016776 0 ustar root root include(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.1.0/cmake/FindSoup.cmake 0000644 0000000 0000000 00000003014 13614354364 014757 0 ustar root root include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Soup
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(Soup_FOUND AND NOT Soup_VERSION)
find_file(Soup_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup_INCLUDE_DIRS})
mark_as_advanced(Soup_VERSION_HEADER)
if(Soup_VERSION_HEADER)
file(STRINGS "${Soup_VERSION_HEADER}" Soup_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MAJOR_VERSION "${Soup_MAJOR_VERSION}")
file(STRINGS "${Soup_VERSION_HEADER}" Soup_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MINOR_VERSION "${Soup_MINOR_VERSION}")
file(STRINGS "${Soup_VERSION_HEADER}" Soup_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MICRO_VERSION "${Soup_MICRO_VERSION}")
set(Soup_VERSION "${Soup_MAJOR_VERSION}.${Soup_MINOR_VERSION}.${Soup_MICRO_VERSION}")
unset(Soup_MAJOR_VERSION)
unset(Soup_MINOR_VERSION)
unset(Soup_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Soup
REQUIRED_VARS Soup_LIBRARY
VERSION_VAR Soup_VERSION)
dino-0.1.0/cmake/FindVala.cmake 0000644 0000000 0000000 00000006252 13614354364 014723 0 ustar root root ##
# 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.1.0/cmake/GenerateGXML.cmake 0000644 0000000 0000000 00000012401 13614354364 015452 0 ustar root root include(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.1.0/cmake/GlibCompileResourcesSupport.cmake 0000644 0000000 0000000 00000000624 13614354364 020712 0 ustar root root # 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.1.0/cmake/LargeFileOffsets.c 0000644 0000000 0000000 00000000437 13614354364 015564 0 ustar root root #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.1.0/cmake/MultiFind.cmake 0000644 0000000 0000000 00000002776 13614354364 015141 0 ustar root root include(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})
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})
endif()
endforeach(pkg)
set(${result} "${_res}" PARENT_SCOPE)
set(${result}_LIBS "${_res_libs}" PARENT_SCOPE)
endfunction()
dino-0.1.0/cmake/PkgConfigWithFallback.cmake 0000644 0000000 0000000 00000011627 13614354364 017364 0 ustar root root include(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 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 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.1.0/cmake/PkgConfigWithFallbackOnConfigScript.cmake 0000644 0000000 0000000 00000012620 13614354364 022166 0 ustar root root include(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.1.0/cmake/UseGettext.cmake 0000644 0000000 0000000 00000002374 13614354364 015341 0 ustar root root function(_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.1.0/cmake/UseVala.cmake 0000644 0000000 0000000 00000031477 13614354364 014606 0 ustar root root ##
# 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.1.0/cmake/cmake_uninstall.cmake.in 0000644 0000000 0000000 00000002014 13614354364 017005 0 ustar root root if(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.1.0/configure 0000755 0000000 0000000 00000024622 13614354364 013065 0 ustar root root #!/bin/sh
OPTS=`getopt -o "h" --long \
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,\
enable-plugin:,disable-plugin:,\
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
bindir:,libdir:,includedir:,datadir:,\
host:,build:,\
sbindir:,sysconfdir:,libexecdir:,localstatedir:,sharedstatedir:,mandir:,infodir:,\
enable-dependency-tracking,disable-dependency-tracking,\
disable-silent-rules,disable-maintainer-mode\
-n './configure' -- "$@"`
if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; 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=
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-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, --enable-dependency-tracking,
--disable-dependency-tracking
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 ;;
--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 ;;
# Ignore for autotools compat
--host | --build ) shift; shift ;;
--disable-dependency-tracking | --enable-dependency-tracking ) shift ;;
# Ignore for debian compat
--disable-silent-rules | --disable-maintainer-mode ) 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" \
-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.1.0/dino.doap 0000644 0000000 0000000 00000044607 13614354364 012761 0 ustar root root
Dino
dino
Modern XMPP Chat Client
็ฐไปฃ XMPP ่ๅคฉๅฎขๆท็ซฏ
ะกะพะฒัะตะผะตะฝะฝัะน ัะฐั-ะบะปะธะตะฝั ะฝะฐ XMPP
Client XMPP de discuศii modern
Moderno cliente de chat XMPP
Nowoczesny komunikator XMPP
Modernen XMPP-chatcliรซnt
Moderne XMPP-chatcliรซnt
Moderne XMPP-sludreklient
Modernen XMPP Chat Client
็พไปฃ็ใช XMPP ใใฃใใ ใฏใฉใคใขใณใ
Client di chat moderno per XMPP
Modern XMPP รzenetkรผldล
Cliente moderno para parolas XMPP
Client XMPP moderne
XMPP txat bezero modernoa
Cliente de XMPP moderno
Moderner XMPP Chat Client
ุชุทุจูู ุญุฏูุซ ููุฏุฑุฏุดุฉ ุนุจุฑ 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 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 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 dispositivos.
Dino jest nowoczesnym, otwartym komunikatorem. Skupia siฤ na prostej obsลudze sieci Jabber/XMPP dbajฤ
c o twojฤ
prywatnoลฤ.
Dino obsลuguje szyfrowanie end-to-end za pomocฤ
OMEMO i OpenPGP, a takลผe daje kontrolฤย nad funkcjami wpลywajฤ
cymi na prywatnoลฤ, takimi jak powiadomienia o pisaniu czy odczytaniu wiadomoลci.
Dino pobiera historiฤ rozmรณw z serwera i synchronizuje wiadomoลci z innymi urzฤ
dzeniami.
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 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 staat u toe privacy-gerelateerde functies, zoals leesbevestigingen en typmeldingen, 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 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 รจ 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.
A Dino egy modern, nyรญlt forrรกskรณdรบ รผzenetkรผldล alkalmazรกs asztali rendszerekre, ami a hangsรบlyt a letisztult รฉs megbรญzhatรณ Jabber/XMPP รฉlmรฉnyre helyezi, mikรถzben a magรกnszfรฉra megลrzรฉsรฉt is fontosnak tartja.
Tรกmogatja a vรฉgponttรณl-vรฉgpontig titkosรญtรกst az OMEMO รฉs az OpenPGP รกltal, รฉs magรกnszfรฉrรกhoz kรถtลdล beรกllรญtรกsi lehetลsรฉgeket is biztosรญt, mint pรฉldรกul a kรฉzbesรญtรฉsi, vagy gรฉpelรฉsi รฉrtesรญtรฉsek kรผldรฉse.
A Dino lekรฉri a chat elลzmรฉnyeket a szerverrล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 chat libre et moderne pour le bureau. Il tente de fournir une expรฉrience Jabber/XMPP simple et fiable tout en ayant toujours ร lโesprit votre vie privรฉe.
Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et permet de configurer les fonctions liรฉes ร la vie privรฉe telles que lโaccusรฉ de rรฉception et les notifications de frappe.
Dino rรฉcupรจre lโhistorique du serveur et synchronise les messages avec d'autres appareils.
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.
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. Estรก enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo tu privacidad en mente.
Soporta cifrado de extremo a extremo 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 los mensajes desde el servidor y sincroniza los mensajes con otros dispositivos.
Dino ist ein moderner, quelloffener Chat Client. Er bietet eine aufgerรคumte und robuste Jabber/XMPP Erfahrung 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 รฉ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.
ุฏููู ุจุฑูุงู
ุฌ ุญุฏูุซ ูู
ูุชูุญ ุงูู
ุตุฏุฑ ููุฏุฑุฏุดุฉ ุตูู
ูู
ูุณุทุญ ุงูู
ูุชุจ. ูููุฑููุฒ ุนูู ุชูุฏูู
ุชุฌุฑุจุฉ ูุธููุฉ ูู
ูุซูู ู
ููุง ูุฌุงุจุฑ/XMPP ู
ุน ุฃุฎุฐ ุฎุตูุตูุชูู
ุจุนูู ุงูุฅุนุชุจุงุฑ.
ููู ูุฏุนู
ุงูุชุดููุฑ ุจูุงุณุทุฉ OMEMO ู OpenPGP ูุณู
ุญ ุจุฅุนุฏุงุฏ ู
ูุฒุงุช ุงูุฎุตูุตูุฉ ูุงูุฅูุตุงูุงุช ุงูู
ูุฑูุกุฉ ูุงูุฅุฎุทุงุฑุงุช ุนูุฏ ุงููุชุงุจุฉ.
ูููู
ุฏููู ุจุฌูุจ ุงูุณูุฌูู ู
ูู ุงูุณูุฑูุฑ ุซู
ููุฒุงู
ูู ุงูุฑุณุงุฆู ู
ุน ุงูุฃุฌูุฒุฉ ุงูุฃุฎุฑู.
Vala
Linux
FreeBSD
complete
complete
complete
partial
complete
partial
Only for viewing avatars
partial
complete
complete
complete
complete
complete
complete
partial
complete
partial
complete
complete
complete
complete
partial
Not for MUCs
complete
complete
complete
partial
Only for outgoing messages
complete
dino-0.1.0/libdino/ 0000755 0000000 0000000 00000000000 13614354364 012570 5 ustar root root dino-0.1.0/libdino/CMakeLists.txt 0000644 0000000 0000000 00000005305 13614354364 015333 0 ustar root root find_packages(LIBDINO_PACKAGES REQUIRED
GDKPixbuf2
Gee
GLib
GModule
GObject
)
vala_precompile(LIBDINO_VALA_C
SOURCES
src/application.vala
src/dbus/login1.vala
src/dbus/upower.vala
src/entity/account.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/avatar_storage.vala
src/service/blocking_manager.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/file_manager.vala
src/service/jingle_file_transfers.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/registration.vala
src/service/roster_manager.vala
src/service/search_processor.vala
src/service/stream_interactor.vala
src/service/util.vala
src/util.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")
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})
dino-0.1.0/libdino/src/ 0000755 0000000 0000000 00000000000 13614354364 013357 5 ustar root root dino-0.1.0/libdino/src/application.vala 0000644 0000000 0000000 00000012724 13614354364 016535 0 ustar root root using Dino.Entities;
namespace Dino {
extern const string VERSION;
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);
CounterpartInteractionManager.start(stream_interactor);
PresenceManager.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);
ChatInteraction.start(stream_interactor);
FileManager.start(stream_interactor, db);
ContentItemStore.start(stream_interactor, db);
NotificationEvents.start(stream_interactor);
SearchProcessor.start(stream_interactor, db);
Register.start(stream_interactor, db);
create_actions();
startup.connect(() => {
stream_interactor.connection_manager.log_options = print_xmpp;
Idle.add(() => {
restore();
return false;
});
});
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.1.0/libdino/src/dbus/ 0000755 0000000 0000000 00000000000 13614354364 014314 5 ustar root root dino-0.1.0/libdino/src/dbus/login1.vala 0000644 0000000 0000000 00000000711 13614354364 016351 0 ustar root root namespace Dino {
[DBus (name = "org.freedesktop.login1.Manager")]
public interface Login1Manager : Object {
public signal void PrepareForSleep(bool suspend);
}
public static Login1Manager? get_login1() {
Login1Manager? login1 = null;
try {
login1 = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1");
} catch (IOError e) {
stderr.printf("%s\n", e.message);
}
return login1;
}
} dino-0.1.0/libdino/src/dbus/upower.vala 0000644 0000000 0000000 00000000675 13614354364 016512 0 ustar root root namespace 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.1.0/libdino/src/dino_i18n.h 0000644 0000000 0000000 00000000405 13614354364 015317 0 ustar root root #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))
#endif dino-0.1.0/libdino/src/entity/ 0000755 0000000 0000000 00000000000 13614354364 014673 5 ustar root root dino-0.1.0/libdino/src/entity/account.vala 0000644 0000000 0000000 00000010315 13614354364 017174 0 ustar root root using 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 ?? 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.1.0/libdino/src/entity/conversation.vala 0000644 0000000 0000000 00000016646 13614354364 020267 0 ustar root root using 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; }
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 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; }
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];
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);
notify_setting = (NotifySetting) row[db.conversation.notification];
send_typing = (Setting) row[db.conversation.send_typing];
send_marker = (Setting) row[db.conversation.send_marker];
notify.connect(on_update);
}
public void persist(Database db) {
this.db = db;
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.notification, notify_setting)
.value(db.conversation.send_typing, send_typing)
.value(db.conversation.send_marker, send_marker);
if (read_up_to != null) {
insert.value(db.conversation.read_up_to, read_up_to.id);
}
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 "nickname":
update.set(db.conversation.resource, nickname); break;
case "active":
update.set(db.conversation.active, active); 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;
}
update.perform();
}
}
}
dino-0.1.0/libdino/src/entity/encryption.vala 0000644 0000000 0000000 00000000141 13614354364 017726 0 ustar root root namespace Dino.Entities {
public enum Encryption {
NONE,
PGP,
OMEMO
}
} dino-0.1.0/libdino/src/entity/file_transfer.vala 0000644 0000000 0000000 00000015026 13614354364 020367 0 ustar root root using 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;
}
}
public string file_name { get; set; }
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; }
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.1.0/libdino/src/entity/message.vala 0000644 0000000 0000000 00000016051 13614354364 017167 0 ustar root root using 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
}
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; }
public string? body { get; set; }
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;
}
}
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_.is_muc_semantic() && 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);
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 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.insert().or("REPLACE")
.value(db.real_jid.message_id, id)
.value(db.real_jid.real_jid, real_jid.to_string())
.perform();
}
}
}
}
dino-0.1.0/libdino/src/entity/settings.vala 0000644 0000000 0000000 00000003652 13614354364 017406 0 ustar root root namespace 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);
}
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.insert().or("REPLACE").value(db.settings.key, "send_typing").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.insert().or("REPLACE").value(db.settings.key, "send_marker").value(db.settings.value, value.to_string()).perform();
send_marker_ = value;
}
}
private bool notifications_;
public bool notifications {
get { return notifications_; }
set {
db.settings.insert().or("REPLACE").value(db.settings.key, "notifications").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.insert().or("REPLACE").value(db.settings.key, "convert_utf8_smileys").value(db.settings.value, value.to_string()).perform();
convert_utf8_smileys_ = value;
}
}
}
}
dino-0.1.0/libdino/src/plugin/ 0000755 0000000 0000000 00000000000 13614354364 014655 5 ustar root root dino-0.1.0/libdino/src/plugin/interfaces.vala 0000644 0000000 0000000 00000011603 13614354364 017646 0 ustar root root using Dino.Entities;
using Xmpp;
namespace Dino.Plugins {
public enum Priority {
LOWEST,
LOWER,
DEFAULT,
HIGHER,
HIGHEST
}
public enum WidgetType {
GTK
}
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 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 AccountSettingsWidget? get_widget(WidgetType type);
}
public interface AccountSettingsWidget : Object {
public abstract void set_account(Account account);
public abstract signal void activated();
public abstract void deactivate();
}
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 ConversationTitlebarWidget? get_widget(WidgetType type);
}
public interface ConversationTitlebarWidget : Object {
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 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 bool dim { get; set; default=false; }
public virtual DateTime sort_time { get; set; default = new DateTime.now_utc(); }
public virtual long seccondary_sort_indicator { get; set; }
public virtual long tertiary_sort_indicator { get; set; }
public virtual DateTime? display_time { get; set; default = null; }
public virtual Encryption encryption { get; set; default = Encryption.NONE; }
public virtual Entities.Message.Marked mark { get; set; default = Entities.Message.Marked.NONE; }
public abstract bool can_merge { get; set; }
public abstract bool requires_avatar { get; set; }
public abstract bool requires_header { get; set; }
public abstract Object? get_widget(WidgetType type);
}
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 InputFieldStatus(string? message, MessageType message_type, InputState input_state) {
this.message = message;
this.message_type = message_type;
this.input_state = input_state;
}
}
}
dino-0.1.0/libdino/src/plugin/loader.vala 0000644 0000000 0000000 00000005321 13614354364 016771 0 ustar root root using 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 loadAll() 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.1.0/libdino/src/plugin/registry.vala 0000644 0000000 0000000 00000007103 13614354364 017373 0 ustar root root using Gee;
namespace Dino.Plugins {
public class Registry {
internal ArrayList encryption_list_entries = new ArrayList();
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) => {
if (a.order < b.order) {
return 1;
} else if (a.order > b.order) {
return -1;
} else {
return 0;
}
});
public bool register_encryption_list_entry(EncryptionListEntry entry) {
lock(encryption_list_entries) {
foreach(var e in encryption_list_entries) {
if (e.encryption == entry.encryption) return false;
}
encryption_list_entries.add(entry);
encryption_list_entries.sort((a,b) => b.name.collate(a.name));
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.1.0/libdino/src/service/ 0000755 0000000 0000000 00000000000 13614354364 015017 5 ustar root root dino-0.1.0/libdino/src/service/avatar_manager.vala 0000644 0000000 0000000 00000016005 13614354364 020636 0 ustar root root using Gdk;
using Gee;
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(Pixbuf avatar, Jid jid, Account account);
private enum Source {
USER_AVATARS,
VCARD
}
private StreamInteractor stream_interactor;
private Database db;
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 AvatarStorage avatar_storage = new AvatarStorage(get_storage_dir());
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);
}
public static string get_storage_dir() {
return Path.build_filename(Dino.get_storage_dir(), "avatars");
}
private AvatarManager(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(initialize_avatar_modules);
}
private void initialize_avatar_modules(Account account, ArrayList modules) {
modules.add(new Xep.UserAvatars.Module(avatar_storage));
modules.add(new Xep.VCard.Module(avatar_storage));
}
private async Pixbuf? get_avatar_by_hash(string hash) {
if (cached_pixbuf.has_key(hash)) {
return cached_pixbuf[hash];
}
if (pending_pixbuf.has_key(hash)) {
pending_pixbuf[hash].add(new SourceFuncWrapper(get_avatar_by_hash.callback));
yield;
return cached_pixbuf[hash];
}
pending_pixbuf[hash] = new ArrayList();
Pixbuf? image = yield avatar_storage.get_image(hash);
if (image != null) {
cached_pixbuf[hash] = image;
} else {
db.avatar.delete().with(db.avatar.hash, "=", hash).perform();
}
foreach (SourceFuncWrapper sfw in pending_pixbuf[hash]) {
sfw.sfun();
}
return image;
}
public bool has_avatar(Account account, Jid jid) {
string? hash = get_avatar_hash(account, jid);
if (hash != null) {
if (cached_pixbuf.has_key(hash)) {
return true;
}
return avatar_storage.has_image(hash);
}
return false;
}
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;
}
string? hash = get_avatar_hash(account, jid_);
if (hash != null) {
return yield get_avatar_by_hash(hash);
}
return null;
}
private string? get_avatar_hash(Account account, Jid jid) {
string? user_avatars_id = user_avatars[jid];
if (user_avatars_id != null) {
return user_avatars_id;
}
string? vcard_avatars_id = vcard_avatars[jid];
if (vcard_avatars_id != null) {
return vcard_avatars_id;
}
return null;
}
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) {
stream.get_module(Xep.UserAvatars.Module.IDENTITY).publish_png(stream, buffer, pixbuf.width, pixbuf.height);
on_user_avatar_received(account, account.bare_jid, Base64.encode(buffer));
}
} 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.connect((stream, jid, id) =>
on_user_avatar_received(account, jid, id)
);
stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar.connect((stream, jid, id) =>
on_vcard_avatar_received(account, jid, id)
);
foreach (var entry in db.get_avatar_hashes(Source.USER_AVATARS).entries) {
on_user_avatar_received(account, entry.key, entry.value);
}
foreach (var entry in db.get_avatar_hashes(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, "=", entry.key.to_string()).perform();
continue;
}
on_vcard_avatar_received(account, entry.key, entry.value);
}
}
private void on_user_avatar_received(Account account, Jid jid, string id) {
if (!user_avatars.has_key(jid) || user_avatars[jid] != id) {
user_avatars[jid] = id;
db.set_avatar_hash(jid, id, Source.USER_AVATARS);
}
avatar_storage.get_image.begin(id, (obj, res) => {
Pixbuf? avatar = avatar_storage.get_image.end(res);
if (avatar != null) {
received_avatar(avatar, jid, account);
}
});
}
private void on_vcard_avatar_received(Account account, Jid jid, string id) {
if (!vcard_avatars.has_key(jid) || vcard_avatars[jid] != id) {
vcard_avatars[jid] = id;
if (!jid.is_full()) { // don't save MUC occupant avatars
db.set_avatar_hash(jid, id, Source.VCARD);
}
}
avatar_storage.get_image.begin(id, (obj, res) => {
Pixbuf? avatar = avatar_storage.get_image.end(res);
if (avatar != null) {
received_avatar(avatar, jid, account);
}
});
}
}
}
dino-0.1.0/libdino/src/service/avatar_storage.vala 0000644 0000000 0000000 00000003125 13614354364 020667 0 ustar root root using Gdk;
using Xmpp;
namespace Dino {
public class AvatarStorage : Xep.PixbufStorage, Object {
string folder;
public AvatarStorage(string folder) {
this.folder = folder;
DirUtils.create_with_parents(folder, 0700);
}
public void store(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();
uint8 fbuf[1024];
size_t size;
Checksum checksum = new Checksum (ChecksumType.SHA1);
while ((size = yield stream.read_async(fbuf)) > 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.1.0/libdino/src/service/blocking_manager.vala 0000644 0000000 0000000 00000003240 13614354364 021145 0 ustar root root using 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, new ArrayList.wrap(new string[] {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, new ArrayList.wrap(new string[] {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.1.0/libdino/src/service/chat_interaction.vala 0000644 0000000 0000000 00000025760 13614354364 021214 0 ustar root root using 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);
}
public bool has_unread(Conversation conversation) {
ContentItem? last_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation);
if (last_content_item == null) return false;
MessageItem? message_item = last_content_item as MessageItem;
if (message_item != null) {
Message last_message = message_item.message;
// We are the message sender
if (last_message.from.equals_bare(conversation.account.bare_jid)) return false;
// We read up to the message
if (conversation.read_up_to != null && last_message.equals(conversation.read_up_to)) return false;
return true;
}
FileItem? file_item = last_content_item as FileItem;
if (file_item != null) {
FileTransfer file_transfer = file_item.file_transfer;
// We are the file sender
if (file_transfer.from.equals_bare(conversation.account.bare_jid)) return false;
if (file_transfer.provider == 0) {
// HTTP file transfer: Check if the associated message is the last one
if (file_transfer.info == null) return false;
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
if (message == null) return false;
if (message.equals(conversation.read_up_to)) return false;
}
if (file_transfer.provider == 1) {
if (file_transfer.state == FileTransfer.State.COMPLETE) return false;
}
return true;
}
return false;
}
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 on_message_sent(Entities.Message message, Conversation conversation) {
last_input_interaction.unset(conversation);
last_interface_interaction.unset(conversation);
conversation.read_up_to = message;
}
private void on_conversation_focused(Conversation? conversation) {
focus_in = true;
if (conversation == null) return;
focused_in(selected_conversation);
check_send_read();
selected_conversation.read_up_to = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(conversation);
}
private void on_conversation_unfocused(Conversation? conversation) {
focus_in = false;
if (conversation == null) return;
focused_out(selected_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 || selected_conversation.type_ == Conversation.Type.GROUPCHAT) return;
Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(selected_conversation);
if (message != null && message.direction == Entities.Message.DIRECTION_RECEIVED && !message.equals(selected_conversation.read_up_to)) {
selected_conversation.read_up_to = message;
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" };
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 (Xep.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();
conversation.read_up_to = message;
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;
if (message.stanza_id == null) return; // Need a stanza id to mark
switch (marker) {
case Xep.ChatMarkers.MARKER_RECEIVED:
if (stanza != null && Xep.ChatMarkers.Module.requests_marking(stanza)) {
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) {
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.1.0/libdino/src/service/connection_manager.vala 0000644 0000000 0000000 00000026502 13614354364 021522 0 ustar root root using Gee;
using Xmpp;
using Dino.Entities;
namespace Dino {
public class ConnectionManager : Object {
public signal void stream_opened(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 HashSet connection_todo = new HashSet(Account.hash_func, Account.equals_func);
private HashMap connections = new HashMap(Account.hash_func, Account.equals_func);
private HashMap connection_errors = 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 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 class Connection(XmppStream stream, DateTime established) {
this.stream = stream;
this.established = established;
}
}
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);
}
login1 = get_login1();
if (login1 != null) {
login1.PrepareForSleep.connect(on_prepare_for_sleep);
}
Timeout.add_seconds(60, () => {
foreach (Account account in connection_todo) {
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 connection_todo;
}
public void connect_account(Account account) {
if (!connection_todo.contains(account)) connection_todo.add(account);
if (!connections.has_key(account)) {
connect_(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) {
Xmpp.Presence.Stanza presence = new Xmpp.Presence.Stanza();
presence.type_ = Xmpp.Presence.Stanza.TYPE_UNAVAILABLE;
change_connection_state(account, ConnectionState.DISCONNECTED);
connections[account].stream.get_module(Presence.Module.IDENTITY).send_presence(connections[account].stream, presence);
}
public async void disconnect_account(Account account) {
if (connections.has_key(account)) {
make_offline(account);
try {
yield connections[account].stream.disconnect();
} catch (Error e) {
debug("Error disconnecting stream: %s", e.message);
}
connection_todo.remove(account);
if (connections.has_key(account)) {
connections.unset(account);
}
}
}
private void connect_(Account account, string? resource = null) {
if (connections.has_key(account)) connections[account].stream.detach_modules();
connection_errors.unset(account);
if (resource == null) resource = account.resourcepart;
XmppStream stream = new XmppStream();
foreach (XmppStreamModule module in module_manager.get_modules(account, resource)) {
stream.add_module(module);
}
stream.log = new XmppLog(account.bare_jid.to_string(), log_options);
debug("[%s] New connection with resource %s: %p", account.bare_jid.to_string(), resource, stream);
Connection connection = new Connection(stream, new DateTime.now_utc());
connections[account] = connection;
change_connection_state(account, ConnectionState.CONNECTING);
stream.attached_modules.connect((stream) => {
change_connection_state(account, ConnectionState.CONNECTED);
});
stream.get_module(Sasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => {
set_connection_error(account, new ConnectionError(ConnectionError.Source.SASL, null));
});
stream.get_module(Tls.Module.IDENTITY).invalid_certificate.connect(() => {
set_connection_error(account, new ConnectionError(ConnectionError.Source.TLS, null) { reconnect_recomendation=ConnectionError.Reconnect.NEVER});
});
stream.received_node.connect(() => {
connection.last_activity = new DateTime.now_utc();
});
connect_async.begin(account, stream);
stream_opened(account, stream);
}
private async void connect_async(Account account, XmppStream stream) {
try {
yield stream.connect(account.domainpart);
} catch (Error e) {
debug("[%s %p] Error: %s", account.bare_jid.to_string(), stream, e.message);
change_connection_state(account, ConnectionState.DISCONNECTED);
if (!connection_todo.contains(account)) {
return;
}
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_(account, account.resourcepart + "-" + random_uuid());
return;
}
}
ConnectionError? error = connection_errors[account];
if (error != null && error.source == ConnectionError.Source.SASL) {
return;
}
debug("[%s] Check reconnect in 5 sec", account.bare_jid.to_string());
Timeout.add_seconds(5, () => {
check_reconnect(account);
return false;
});
}
}
private void check_reconnects() {
foreach (Account account in connection_todo) {
check_reconnect(account);
}
}
private void check_reconnect(Account account) {
if (!connections.has_key(account)) return;
bool acked = false;
DateTime? last_activity_was = connections[account].last_activity;
XmppStream stream = connections[account].stream;
stream.get_module(Xep.Ping.Module.IDENTITY).send_ping(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].stream.disconnect.begin((_, res) => {
try {
connections[account].stream.disconnect.end(res);
} catch (Error e) {
debug("Error disconnecting stream: %s", e.message);
}
});
connect_(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 connection_todo) {
change_connection_state(account, ConnectionState.DISCONNECTED);
}
}
}
private async void on_prepare_for_sleep(bool suspend) {
foreach (Account account in connection_todo) {
change_connection_state(account, ConnectionState.DISCONNECTED);
}
if (suspend) {
debug("Login1: Device suspended");
foreach (Account account in connection_todo) {
try {
make_offline(account);
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);
}
}
}
dino-0.1.0/libdino/src/service/content_item_store.vala 0000644 0000000 0000000 00000033132 13614354364 021572 0 ustar root root using 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 Gee.List filters = new ArrayList();
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);
}
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);
}
public Gee.List get_items_from_query(QueryBuilder select, Conversation conversation) {
Gee.TreeSet items = new Gee.TreeSet(ContentItem.compare);
foreach (var row in select) {
int provider = row[db.content_item.content_type];
int foreign_id = row[db.content_item.foreign_id];
switch (provider) {
case 1:
RowOption row_option = db.message.select().with(db.message.id, "=", foreign_id).row();
if (row_option.is_present()) {
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(foreign_id, conversation);
if (message == null) {
try {
message = new Message.from_row(db, row_option.inner);
} catch (InvalidJidError e) {
warning("Ignoring message with invalid Jid: %s", e.message);
}
}
if (message != null) {
items.add(new MessageItem(message, conversation, row[db.content_item.id]));
}
}
break;
case 2:
RowOption row_option = db.file_transfer.select().with(db.file_transfer.id, "=", foreign_id).row();
if (row_option.is_present()) {
try {
string storage_dir = FileManager.get_storage_dir();
FileTransfer file_transfer = new FileTransfer.from_row(db, row_option.inner, storage_dir);
if (conversation.type_.is_muc_semantic()) {
try {
// resourcepart wasn't set before, so we pick nickname instead (which isn't accurate if nickname is changed)
file_transfer.ourpart = conversation.counterpart.with_resource(file_transfer.ourpart.resourcepart ?? conversation.nickname);
} catch (InvalidJidError e) {
warning("Failed setting file transfer Jid: %s", e.message);
}
}
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);
}
items.add(new FileItem(file_transfer, conversation, row[db.content_item.id], message));
} catch (InvalidJidError e) {
warning("Ignoring file transfer with invalid Jid: %s", e.message);
}
}
break;
}
}
Gee.List ret = new ArrayList();
foreach (ContentItem item in items) {
ret.add(item);
}
return ret;
}
public ContentItem? get_item(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_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.local_time, "DESC")
.order_by(db.content_item.time, "DESC")
.limit(count);
return get_items_from_query(select, conversation);
}
public Gee.List get_before(Conversation conversation, ContentItem item, int count) {
long local_time = (long) item.sort_time.to_unix();
long time = (long) item.display_time.to_unix();
QueryBuilder select = db.content_item.select()
.where(@"local_time < ? OR (local_time = ? AND time < ?) OR (local_time = ? AND time = ? AND id < ?)", { local_time.to_string(), local_time.to_string(), time.to_string(), local_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.local_time, "DESC")
.order_by(db.content_item.time, "DESC")
.limit(count);
return get_items_from_query(select, conversation);
}
public Gee.List get_after(Conversation conversation, ContentItem item, int count) {
long local_time = (long) item.sort_time.to_unix();
long time = (long) item.display_time.to_unix();
QueryBuilder select = db.content_item.select()
.where(@"local_time > ? OR (local_time = ? AND time > ?) OR (local_time = ? AND time = ? AND id > ?)", { local_time.to_string(), local_time.to_string(), time.to_string(), local_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.local_time, "ASC")
.order_by(db.content_item.time, "ASC")
.limit(count);
return get_items_from_query(select, conversation);
}
public void add_filter(ContentFilter content_filter) {
filters.add(content_filter);
}
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 (!discard(item)) {
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 (!discard(item)) {
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();
}
private bool discard(ContentItem content_item) {
foreach (ContentFilter filter in filters) {
if (filter.discard(content_item)) {
return true;
}
}
return false;
}
}
public interface ContentItemCollection : Object {
public abstract void insert_item(ContentItem item);
public abstract void remove_item(ContentItem item);
}
public interface ContentFilter : Object {
public abstract bool discard(ContentItem content_item);
}
public abstract class ContentItem : Object {
public int id { get; set; }
public string type_ { get; set; }
public Jid jid { get; set; }
public DateTime sort_time { get; set; }
public DateTime display_time { get; set; }
public Encryption encryption { get; set; }
public Entities.Message.Marked mark { get; set; }
ContentItem(int id, string ty, Jid jid, DateTime sort_time, DateTime display_time, Encryption encryption, Entities.Message.Marked mark) {
this.id = id;
this.type_ = ty;
this.jid = jid;
this.sort_time = sort_time;
this.display_time = display_time;
this.encryption = encryption;
this.mark = mark;
}
public static int compare(ContentItem a, ContentItem b) {
int res = a.sort_time.compare(b.sort_time);
if (res == 0) {
res = a.display_time.compare(b.display_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.local_time, message.time, message.encryption, message.marked);
this.message = message;
this.conversation = conversation;
WeakRef weak_message = WeakRef(message);
message.notify["marked"].connect(() => {
Message? m = weak_message.get() as Message;
if (m == null) return;
mark = m.marked;
});
}
}
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.local_time, file_transfer.time, file_transfer.encryption, mark);
this.file_transfer = file_transfer;
this.conversation = conversation;
if (message != null) {
WeakRef weak_message = WeakRef(message);
message.notify["marked"].connect(() => {
Message? m = weak_message.get() as Message;
if (m == null) return;
this.mark = m.marked;
});
} else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) {
file_transfer.notify["state"].connect_after(() => {
this.mark = file_to_message_state(file_transfer.state);
});
}
}
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();
}
}
}
dino-0.1.0/libdino/src/service/conversation_manager.vala 0000644 0000000 0000000 00000020205 13614354364 022067 0 ustar root root using 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_for_presence(Show show, Account account) {
return get_conversations(show.jid, account);
}
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? 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 = Xep.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null;
bool is_recent = message.local_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.local_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.1.0/libdino/src/service/counterpart_interaction_manager.vala 0000644 0000000 0000000 00000022165 13614354364 024331 0 ustar root root using 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(Account account, Jid jid, 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> chat_states = 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).received_pipeline.connect(new ReceivedMessageListener(this));
stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(check_if_got_marker);
stream_interactor.stream_negotiated.connect(() => chat_states.clear() );
}
public HashMap? get_chat_states(Conversation conversation) {
if (stream_interactor.connection_manager.get_state(conversation.account) != ConnectionManager.ConnectionState.CONNECTED) return null;
return chat_states[conversation];
}
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) => {
on_chat_marker_received(account, jid, marker, id);
});
stream_interactor.module_manager.get_module(account, Xep.MessageDeliveryReceipts.Module.IDENTITY).receipt_received.connect((stream, jid, id) => {
on_receipt_received(account, jid, id);
});
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 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;
Message 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(message);
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 (!chat_states.has_key(conversation)) {
chat_states[conversation] = new HashMap(Jid.hash_func, Jid.equals_func);
}
if (state == Xmpp.Xep.ChatStateNotifications.STATE_ACTIVE) {
chat_states[conversation].unset(jid);
} else {
chat_states[conversation][jid] = state;
}
received_state(account, jid, state);
}
private void on_chat_marker_received(Account account, 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 = account.bare_jid.to_string() == jid.bare_jid.to_string();
if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account)) {
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)) {
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;
Conversation? conversation = stream_interactor.get_module(MessageStorage.IDENTITY).get_conversation_for_stanza_id(account, stanza_id);
if (conversation == null || 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) 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;
} else {
// We received a marker from someone else. Search the respective message and mark it.
foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_conversations(jid, account)) {
// We can't currently handle chat markers in MUCs
if (conversation.type_ == Conversation.Type.GROUPCHAT) continue;
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(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(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)) {
on_chat_marker_received(conversation.account, conversation.counterpart, marker_wo_message[message.stanza_id], message.stanza_id);
marker_wo_message.unset(message.stanza_id);
}
}
private class ReceivedMessageListener : MessageListener {
public string[] after_actions_const = new string[]{ "DEDUPLICATE" };
public override string action_group { get { return "STORE"; } }
public override string[] after_actions { get { return after_actions_const; } }
private CounterpartInteractionManager outer;
public ReceivedMessageListener(CounterpartInteractionManager outer) {
this.outer = outer;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
outer.on_chat_state_received.begin(conversation.account, conversation.counterpart, Xep.ChatStateNotifications.STATE_ACTIVE, stanza);
return false;
}
}
private void on_receipt_received(Account account, Jid jid, string id) {
on_chat_marker_received(account, jid, Xep.ChatMarkers.MARKER_RECEIVED, id);
}
}
}
dino-0.1.0/libdino/src/service/database.vala 0000644 0000000 0000000 00000056017 13614354364 017441 0 ustar root root using Gee;
using Qlite;
using Xmpp;
using Dino.Entities;
namespace Dino {
public class Database : Qlite.Database {
private const int VERSION = 11;
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 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_localtime_counterpart_idx", {local_time, conversation_id});
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_localtime_idx", {account_id, counterpart_id, local_time});
// deduplication
index("message_account_counterpart_stanzaid_idx", {account_id, counterpart_id, stanza_id});
fts({body});
}
}
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 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});
index("filetransfer_localtime_counterpart_idx", {local_time, counterpart_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 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 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 };
internal ConversationTable(Database db) {
base(db, "conversation");
init({id, account_id, jid_id, resource, active, last_active, type_, encryption, read_up_to, notification, send_typing, send_marker});
}
}
public class AvatarTable : Table {
public Column jid = new Column.Text("jid");
public Column hash = new Column.Text("hash");
public Column type_ = new Column.Integer("type");
internal AvatarTable(Database db) {
base(db, "avatar");
init({jid, hash, type_});
}
}
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 from_end = new Column.BoolInt("from_end");
public Column from_id = new Column.Text("from_id");
public Column from_time = new Column.Long("from_time") { not_null = true };
public Column to_id = new Column.Text("to_id");
public Column to_time = new Column.Long("to_time") { not_null = true };
internal MamCatchupTable(Database db) {
base(db, "mam_catchup");
init({id, account_id, from_end, from_id, from_time, to_id, to_time});
}
}
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 AccountTable account { get; private set; }
public JidTable jid { get; private set; }
public ContentItemTable content_item { get; private set; }
public MessageTable message { get; private set; }
public RealJidTable real_jid { get; private set; }
public FileTransferTable file_transfer { get; private set; }
public ConversationTable conversation { get; private set; }
public AvatarTable avatar { get; private set; }
public EntityFeatureTable entity_feature { get; private set; }
public RosterTable roster { get; private set; }
public MamCatchupTable mam_catchup { get; private set; }
public SettingsTable 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);
content_item = new ContentItemTable(this);
message = new MessageTable(this);
real_jid = new RealJidTable(this);
file_transfer = new FileTransferTable(this);
conversation = new ConversationTable(this);
avatar = new AvatarTable(this);
entity_feature = new EntityFeatureTable(this);
roster = new RosterTable(this);
mam_catchup = new MamCatchupTable(this);
settings = new SettingsTable(this);
init({ account, jid, content_item, message, real_jid, file_transfer, conversation, avatar, entity_feature, roster, mam_catchup, settings });
try {
exec("PRAGMA synchronous=0");
} catch (Error e) { }
}
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);
}
}
}
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(@"local_time < ? OR (local_time = ? AND 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(@"local_time > ? OR (local_time = ? AND id > ?)", { after.to_unix().to_string(), after.to_unix().to_string(), id.to_string() });
} else {
select.with(message.local_time, ">", (long) after.to_unix());
}
if (id > 0) {
select.with(message.id, ">", id);
}
} else {
select.order_by(message.local_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);
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 Gee.List get_unsend_messages(Account account, Jid? jid = null) {
Gee.List ret = new ArrayList();
var select = message.select()
.with(message.account_id, "=", account.id)
.with(message.marked, "=", (int) Message.Marked.UNSENT);
if (jid != null) {
select.with(message.counterpart_id, "=", get_jid_id(jid));
}
foreach (Row row in select) {
try {
ret.add(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 void set_avatar_hash(Jid jid, string hash, int type) {
avatar.insert().or("REPLACE")
.value(avatar.jid, jid.to_string())
.value(avatar.hash, hash)
.value(avatar.type_, type)
.perform();
}
public HashMap get_avatar_hashes(int type) {
HashMap ret = new HashMap(Jid.hash_func, Jid.equals_func);
foreach (Row row in avatar.select({avatar.jid, avatar.hash}).with(avatar.type_, "=", type)) {
try {
ret[new Jid(row[avatar.jid])] = row[avatar.hash];
} catch (InvalidJidError e) {
warning("Ignoring avatar of invalid Jid: %s", e.message);
}
}
return ret;
}
public void add_entity_features(string entity, Gee.List features) {
foreach (string feature in features) {
entity_feature.insert()
.value(entity_feature.entity, entity)
.value(entity_feature.feature, feature)
.perform();
}
}
public Gee.List get_entity_features(string entity) {
ArrayList ret = new ArrayList();
foreach (Row row in entity_feature.select({entity_feature.feature}).with(entity_feature.entity, "=", entity)) {
ret.add(row[entity_feature.feature]);
}
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.1.0/libdino/src/service/entity_capabilities_storage.vala 0000644 0000000 0000000 00000000740 13614354364 023436 0 ustar root root using Gee;
using Xmpp;
namespace Dino {
public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object {
private Database db;
public EntityCapabilitiesStorage(Database db) {
this.db = db;
}
public void store_features(string entity, Gee.List features) {
db.add_entity_features(entity, features);
}
public Gee.List get_features(string entity) {
return db.get_entity_features(entity);
}
}
}
dino-0.1.0/libdino/src/service/file_manager.vala 0000644 0000000 0000000 00000045126 13614354364 020305 0 ustar root root using 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 void send_file(string uri, 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 {
File file = File.new_for_path(uri);
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);
file_transfer.persist(db);
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 (sender.can_send(conversation, file_transfer)) {
if (file_transfer.encryption == Encryption.NONE || 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);
conversation.last_active = file_transfer.time;
} 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 bool is_upload_available(Conversation? conversation) {
if (conversation == null) return false;
foreach (FileSender file_sender in file_senders) {
if (file_sender.is_upload_available(conversation)) return true;
}
return false;
}
public Gee.List get_latest_transfers(Account account, Jid counterpart, int n) {
Qlite.QueryBuilder select = db.file_transfer.select()
.with(db.file_transfer.counterpart_id, "=", db.get_jid_id(counterpart))
.with(db.file_transfer.account_id, "=", account.id)
.order_by(db.file_transfer.local_time, "DESC")
.limit(n);
return get_transfers_from_qry(select);
}
public Gee.List get_transfers_before(Account account, Jid counterpart, DateTime before, int n) {
Qlite.QueryBuilder select = db.file_transfer.select()
.with(db.file_transfer.counterpart_id, "=", db.get_jid_id(counterpart))
.with(db.file_transfer.account_id, "=", account.id)
.with(db.file_transfer.local_time, "<", (long)before.to_unix())
.order_by(db.file_transfer.local_time, "DESC")
.limit(n);
return get_transfers_from_qry(select);
}
public Gee.List get_transfers_after(Account account, Jid counterpart, DateTime after, int n) {
Qlite.QueryBuilder select = db.file_transfer.select()
.with(db.file_transfer.counterpart_id, "=", db.get_jid_id(counterpart))
.with(db.file_transfer.account_id, "=", account.id)
.with(db.file_transfer.local_time, ">", (long)after.to_unix())
.limit(n);
return get_transfers_from_qry(select);
}
private Gee.List get_transfers_from_qry(Qlite.QueryBuilder select) {
Gee.List ret = new ArrayList();
foreach (Qlite.Row row in select) {
try {
FileTransfer file_transfer = new FileTransfer.from_row(db, row, get_storage_dir());
ret.insert(0, file_transfer);
} catch (InvalidJidError e) {
warning("Ignoring file transfer with invalid Jid: %s", e.message);
}
}
return ret;
}
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) {
Jid relevant_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(file_transfer.from, conversation.account) ?? conversation.counterpart;
bool in_roster = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, relevant_jid) != null;
return file_transfer.direction == FileTransfer.DIRECTION_SENT || 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);
yield os.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
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.direction = from.bare_jid.equals(conversation.account.bare_jid) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED;
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;
} else {
file_transfer.ourpart = conversation.account.full_jid;
}
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;
file_transfer.persist(db);
if (is_sender_trustworthy(file_transfer, conversation)) {
try {
yield get_file_meta(file_provider, file_transfer, conversation, receive_data);
if (file_transfer.size >= 0 && file_transfer.size < 5000000) {
yield download_file_internal(file_provider, file_transfer, conversation);
}
} catch (Error e) {
warning("Error downloading file: %s", e.message);
file_transfer.state = FileTransfer.State.FAILED;
}
}
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 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 bool is_upload_available(Conversation conversation);
public abstract 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 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 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.1.0/libdino/src/service/jingle_file_transfers.vala 0000644 0000000 0000000 00000023271 13614354364 022227 0 ustar root root using Gdk;
using Gee;
using Xmpp;
using Dino.Entities;
namespace Dino {
public interface JingleFileEncryptionHelper : Object {
public abstract bool can_transfer(Conversation conversation);
public abstract 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 FileMeta complete_meta(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta, Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer);
}
public class JingleFileEncryptionHelperTransferOnly : JingleFileEncryptionHelper, Object {
public bool can_transfer(Conversation conversation) {
return true;
}
public 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 FileMeta complete_meta(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta, Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer) {
return file_meta;
}
}
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.stream_negotiated.connect(on_stream_negotiated);
}
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 {
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");
}
FileMeta meta = file_meta;
foreach (JingleFileEncryptionHelper helper in JingleFileHelperRegistry.instance.encryption_helpers.values) {
meta = helper.complete_meta(file_transfer, receive_data, meta, jingle_file_transfer);
}
return meta;
}
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");
}
foreach (JingleFileEncryptionHelper helper in JingleFileHelperRegistry.instance.encryption_helpers.values) {
helper.complete_meta(file_transfer, receive_data, file_meta, jingle_file_transfer);
}
try {
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_stream_negotiated(Account account, XmppStream stream) {
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) {
// TODO(hrxi): What to do?
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 bool is_upload_available(Conversation conversation) {
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 (stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
return true;
}
}
return false;
}
public bool can_send(Conversation conversation, FileTransfer file_transfer) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) return false;
// No file specific restrictions apply to Jingle file transfers
return is_upload_available(conversation);
}
public bool can_encrypt(Conversation conversation, FileTransfer file_transfer) {
JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption);
if (helper == null) return false;
return 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 && helper.can_encrypt(conversation, file_transfer);
foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) {
// TODO(hrxi): Prioritization of transports (and resources?).
if (!stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) {
continue;
}
if (must_encrypt && !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");
}
}
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);
return;
}
}
public int get_id() { return 1; }
public float get_priority() { return 50; }
}
}
dino-0.1.0/libdino/src/service/message_processor.vala 0000644 0000000 0000000 00000075326 13614354364 021424 0 ustar root root using 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 history_synced(Account account);
public MessageListenerHolder received_pipeline = new MessageListenerHolder();
private StreamInteractor stream_interactor;
private Database db;
private Object lock_send_unsent;
private HashMap current_catchup_id = new HashMap(Account.hash_func, Account.equals_func);
private HashMap> mam_times = new HashMap>();
public HashMap hitted_range = new HashMap();
public HashMap catchup_until_id = new HashMap(Account.hash_func, Account.equals_func);
public HashMap catchup_until_time = new HashMap(Account.hash_func, Account.equals_func);
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;
received_pipeline.connect(new DeduplicateMessageListener(this, db));
received_pipeline.connect(new FilterMessageListener());
received_pipeline.connect(new StoreMessageListener(stream_interactor));
received_pipeline.connect(new MamMessageListener(stream_interactor));
stream_interactor.account_added.connect(on_account_added);
stream_interactor.connection_manager.connection_state_changed.connect((account, state) => {
if (state == ConnectionManager.ConnectionState.CONNECTED) send_unsent_messages(account);
});
stream_interactor.connection_manager.stream_opened.connect((account, stream) => {
debug("MAM: [%s] Reset catchup_id", account.bare_jid.to_string());
current_catchup_id.unset(account);
mam_times[account] = new HashMap();
});
}
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(MessageStorage.IDENTITY).add_message(message, conversation);
send_xmpp_message(message, conversation);
message_sent(message, conversation);
return message;
}
public void send_unsent_messages(Account account, Jid? jid = null) {
Gee.List unsend_messages = db.get_unsend_messages(account, jid);
foreach (Entities.Message message in unsend_messages) {
Conversation? msg_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message.counterpart, account);
if (msg_conv != null) {
send_xmpp_message(message, msg_conv, true);
}
}
}
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);
});
XmppStream? stream_bak = null;
stream_interactor.module_manager.get_module(account, Xmpp.Xep.MessageArchiveManagement.Module.IDENTITY).feature_available.connect( (stream) => {
if (stream == stream_bak) return;
current_catchup_id.unset(account);
stream_bak = stream;
debug("MAM: [%s] MAM available", account.bare_jid.to_string());
do_mam_catchup.begin(account);
});
stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_message_unprocessed.connect((stream, message) => {
if (!message.from.equals(account.bare_jid)) return;
Xep.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xep.MessageArchiveManagement.Flag.IDENTITY) : null;
if (mam_flag == null) return;
string? id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", "id");
if (id == null) return;
StanzaNode? delay_node = message.stanza.get_deep_subnode(mam_flag.ns_ver + ":result", "urn:xmpp:forward:0:forwarded", "urn:xmpp:delay:delay");
if (delay_node == null) return;
DateTime? time = DelayedDelivery.Module.get_time_for_node(delay_node);
if (time == null) return;
mam_times[account][id] = time;
string? query_id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", mam_flag.ns_ver + ":queryid");
if (query_id != null && id == catchup_until_id[account]) {
debug("MAM: [%s] Hitted range (id) %s", account.bare_jid.to_string(), id);
hitted_range[query_id] = -2;
}
});
}
private async void do_mam_catchup(Account account) {
debug("MAM: [%s] Start catchup", account.bare_jid.to_string());
string? earliest_id = null;
DateTime? earliest_time = null;
bool continue_sync = true;
while (continue_sync) {
continue_sync = false;
// Get previous row
var previous_qry = db.mam_catchup.select().with(db.mam_catchup.account_id, "=", account.id).order_by(db.mam_catchup.to_time, "DESC");
if (current_catchup_id.has_key(account)) {
previous_qry.with(db.mam_catchup.id, "!=", current_catchup_id[account]);
}
RowOption previous_row = previous_qry.single().row();
if (previous_row.is_present()) {
catchup_until_id[account] = previous_row[db.mam_catchup.to_id];
catchup_until_time[account] = (new DateTime.from_unix_utc(previous_row[db.mam_catchup.to_time])).add_minutes(-5);
debug("MAM: [%s] Previous entry exists", account.bare_jid.to_string());
} else {
catchup_until_id.unset(account);
catchup_until_time.unset(account);
}
string query_id = Xmpp.random_uuid();
yield get_mam_range(account, query_id, null, null, earliest_time, earliest_id);
if (!hitted_range.has_key(query_id)) {
debug("MAM: [%s] Set catchup end reached", account.bare_jid.to_string());
db.mam_catchup.update()
.set(db.mam_catchup.from_end, true)
.with(db.mam_catchup.id, "=", current_catchup_id[account])
.perform();
}
if (hitted_range.has_key(query_id)) {
if (merge_ranges(account, null)) {
RowOption current_row = db.mam_catchup.row_with(db.mam_catchup.id, current_catchup_id[account]);
bool range_from_complete = current_row[db.mam_catchup.from_end];
if (!range_from_complete) {
continue_sync = true;
earliest_id = current_row[db.mam_catchup.from_id];
earliest_time = (new DateTime.from_unix_utc(current_row[db.mam_catchup.from_time])).add_seconds(1);
}
}
}
}
}
/*
* Merges the row with `current_catchup_id` with the previous range (optional: with `earlier_id`)
* Changes `current_catchup_id` to the previous range
*/
private bool merge_ranges(Account account, int? earlier_id) {
RowOption current_row = db.mam_catchup.row_with(db.mam_catchup.id, current_catchup_id[account]);
RowOption previous_row = null;
if (earlier_id != null) {
previous_row = db.mam_catchup.row_with(db.mam_catchup.id, earlier_id);
} else {
previous_row = db.mam_catchup.select()
.with(db.mam_catchup.account_id, "=", account.id)
.with(db.mam_catchup.id, "!=", current_catchup_id[account])
.order_by(db.mam_catchup.to_time, "DESC").single().row();
}
if (!previous_row.is_present()) {
debug("MAM: [%s] Merging: No previous row", account.bare_jid.to_string());
return false;
}
var qry = db.mam_catchup.update().with(db.mam_catchup.id, "=", previous_row[db.mam_catchup.id]);
debug("MAM: [%s] Merging %ld-%ld with %ld- %ld", account.bare_jid.to_string(), previous_row[db.mam_catchup.from_time], previous_row[db.mam_catchup.to_time], current_row[db.mam_catchup.from_time], current_row[db.mam_catchup.to_time]);
if (current_row[db.mam_catchup.from_time] < previous_row[db.mam_catchup.from_time]) {
qry.set(db.mam_catchup.from_id, current_row[db.mam_catchup.from_id])
.set(db.mam_catchup.from_time, current_row[db.mam_catchup.from_time]);
}
if (current_row[db.mam_catchup.to_time] > previous_row[db.mam_catchup.to_time]) {
qry.set(db.mam_catchup.to_id, current_row[db.mam_catchup.to_id])
.set(db.mam_catchup.to_time, current_row[db.mam_catchup.to_time]);
}
qry.perform();
current_catchup_id[account] = previous_row[db.mam_catchup.id];
db.mam_catchup.delete().with(db.mam_catchup.id, "=", current_row[db.mam_catchup.id]).perform();
return true;
}
private async bool get_mam_range(Account account, string? query_id, DateTime? from_time, string? from_id, DateTime? to_time, string? to_id) {
debug("MAM: [%s] Get range %s - %s", account.bare_jid.to_string(), from_time != null ? from_time.to_string() : "", to_time != null ? to_time.to_string() : "");
XmppStream stream = stream_interactor.get_stream(account);
Iq.Stanza? iq = yield stream.get_module(Xep.MessageArchiveManagement.Module.IDENTITY).query_archive(stream, null, query_id, from_time, from_id, to_time, to_id);
if (iq == null) {
debug(@"MAM: [%s] IQ null", account.bare_jid.to_string());
return true;
}
if (iq.stanza.get_deep_string_content("urn:xmpp:mam:2:fin", "http://jabber.org/protocol/rsm" + ":set", "first") == null) {
return true;
}
while (iq != null) {
string? earliest_id = iq.stanza.get_deep_string_content("urn:xmpp:mam:2:fin", "http://jabber.org/protocol/rsm" + ":set", "first");
if (earliest_id == null) return true;
if (!mam_times[account].has_key(earliest_id)) error("wtf");
debug("MAM: [%s] Update from_id %s", account.bare_jid.to_string(), earliest_id);
if (!current_catchup_id.has_key(account)) {
debug("MAM: [%s] We get our first MAM page", account.bare_jid.to_string());
string? latest_id = iq.stanza.get_deep_string_content("urn:xmpp:mam:2:fin", "http://jabber.org/protocol/rsm" + ":set", "last");
if (!mam_times[account].has_key(latest_id)) error("wtf2");
current_catchup_id[account] = (int) db.mam_catchup.insert()
.value(db.mam_catchup.account_id, account.id)
.value(db.mam_catchup.from_id, earliest_id)
.value(db.mam_catchup.from_time, (long)mam_times[account][earliest_id].to_unix())
.value(db.mam_catchup.to_id, latest_id)
.value(db.mam_catchup.to_time, (long)mam_times[account][latest_id].to_unix())
.perform();
} else {
// Update existing id
db.mam_catchup.update()
.set(db.mam_catchup.from_id, earliest_id)
.set(db.mam_catchup.from_time, (long)mam_times[account][earliest_id].to_unix()) // need to make sure we have this
.with(db.mam_catchup.id, "=", current_catchup_id[account])
.perform();
}
TimeSpan catchup_time_ago = (new DateTime.now_utc()).difference(mam_times[account][earliest_id]);
int wait_ms = 10;
if (catchup_time_ago > 14 * TimeSpan.DAY) {
wait_ms = 2000;
} else if (catchup_time_ago > 5 * TimeSpan.DAY) {
wait_ms = 1000;
} else if (catchup_time_ago > 2 * TimeSpan.DAY) {
wait_ms = 200;
} else if (catchup_time_ago > TimeSpan.DAY) {
wait_ms = 50;
}
mam_times[account] = new HashMap();
Timeout.add(wait_ms, () => {
if (hitted_range.has_key(query_id)) {
debug(@"MAM: [%s] Hitted contains key %s", account.bare_jid.to_string(), query_id);
iq = null;
Idle.add(get_mam_range.callback);
return false;
}
stream.get_module(Xep.MessageArchiveManagement.Module.IDENTITY).page_through_results.begin(stream, null, query_id, from_time, to_time, iq, (_, res) => {
iq = stream.get_module(Xep.MessageArchiveManagement.Module.IDENTITY).page_through_results.end(res);
Idle.add(get_mam_range.callback);
});
return false;
});
yield;
}
return false;
}
private async void on_message_received(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;
// MAM state database update
Xep.MessageArchiveManagement.MessageFlag mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message_stanza);
if (mam_flag == null) {
if (current_catchup_id.has_key(account)) {
string? stanza_id = UniqueStableStanzaIDs.get_stanza_id(message_stanza, account.bare_jid);
if (stanza_id != null) {
db.mam_catchup.update()
.with(db.mam_catchup.id, "=", current_catchup_id[account])
.set(db.mam_catchup.to_time, (long)message.local_time.to_unix())
.set(db.mam_catchup.to_id, stanza_id)
.perform();
}
}
}
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);
}
}
public async Entities.Message parse_message_stanza(Account account, Xmpp.MessageStanza message) {
Entities.Message new_message = new Entities.Message(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);
Xep.MessageArchiveManagement.MessageFlag? mam_message_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message);
Xep.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xep.MessageArchiveManagement.Flag.IDENTITY) : null;
if (mam_message_flag != null && mam_flag != null && mam_flag.ns_ver == Xep.MessageArchiveManagement.NS_URI && mam_message_flag.mam_id != null) {
new_message.server_id = mam_message_flag.mam_id;
} else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
} else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
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 {
SourceFunc callback = determine_message_type.callback;
XmppStream stream = stream_interactor.get_stream(account);
if (stream != null) stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY).get_entity_categories(stream, message.counterpart.bare_jid, (stream, identities) => {
if (identities == null) {
message.type_ = Entities.Message.Type.CHAT;
Idle.add((owned) callback);
return;
}
foreach (Xep.ServiceDiscovery.Identity identity in identities) {
if (identity.category == Xep.ServiceDiscovery.Identity.CATEGORY_CONFERENCE) {
message.type_ = Entities.Message.Type.GROUPCHAT_PM;
} else {
message.type_ = Entities.Message.Type.CHAT;
}
}
Idle.add((owned) callback);
});
yield;
}
}
return Entities.Message.Type.CHAT;
}
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;
private Database db;
public DeduplicateMessageListener(MessageProcessor outer, Database db) {
this.outer = outer;
this.db = db;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
Account account = conversation.account;
Xep.MessageArchiveManagement.MessageFlag? mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(stanza);
// 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);
bool duplicate = builder.count() > 0;
if (duplicate && mam_flag != null) {
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 (outer.catchup_until_time.has_key(account) && mam_flag.server_time.compare(outer.catchup_until_time[account]) < 0) {
outer.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(), outer.catchup_until_time[account].to_string());
}
}
if (duplicate) 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.counterpart.resourcepart != null) {
builder.with(db.message.counterpart_resource, "=", message.counterpart.resourcepart);
} else {
builder.with_null(db.message.counterpart_resource);
}
RowOption row_opt = builder.single().row();
bool duplicate = row_opt.is_present();
if (duplicate && mam_flag != null && row_opt[db.message.server_id] == null &&
outer.catchup_until_time.has_key(account) && mam_flag.server_time.compare(outer.catchup_until_time[account]) > 0) {
outer.hitted_range[mam_flag.query_id] = -1;
debug(@"MAM: [%s] Hitted range duplicate message id. id %s qid %s", account.bare_jid.to_string(), message.stanza_id, mam_flag.query_id);
}
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 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 StreamInteractor stream_interactor;
public StoreMessageListener(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(MessageStorage.IDENTITY).add_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 = Xep.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null;
XmppStream? stream = stream_interactor.get_stream(conversation.account);
Xep.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xep.MessageArchiveManagement.Flag.IDENTITY) : null;
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_.is_muc_semantic()) {
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;
return message;
}
public void send_xmpp_message(Entities.Message message, Conversation conversation, bool delayed = false) {
lock (lock_send_unsent) {
XmppStream stream = stream_interactor.get_stream(conversation.account);
message.marked = Entities.Message.Marked.NONE;
if (stream != null) {
Xmpp.MessageStanza new_message = new Xmpp.MessageStanza(message.stanza_id);
new_message.to = message.counterpart;
new_message.body = message.body;
if (conversation.type_ == Conversation.Type.GROUPCHAT) {
new_message.type_ = Xmpp.MessageStanza.TYPE_GROUPCHAT;
} else {
new_message.type_ = Xmpp.MessageStanza.TYPE_CHAT;
}
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) {
Xmpp.Xep.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) return;
if(!flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.STABLE_ID)) {
Xep.UniqueStableStanzaIDs.set_origin_id(new_message, message.stanza_id);
}
}
stream.get_module(Xmpp.MessageModule.IDENTITY).send_message(stream, new_message);
} else {
message.marked = Entities.Message.Marked.UNSENT;
}
}
}
}
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.1.0/libdino/src/service/message_storage.vala 0000644 0000000 0000000 00000012447 13614354364 021044 0 ustar root root 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 HashMap> messages = new HashMap