Compare commits
23 Commits
d5dd626c3b
...
353329bbea
|
@ -0,0 +1,343 @@
|
|||
This fork of Dino diverged from upstream on this commit:
|
||||
|
||||
f25bfb00969a7e09996da2d5500e6718f4cc0148
|
||||
|
||||
However, this fork of Dino aims to backport all compatible security
|
||||
fixes and improvements from upstream. This is an exhaustive list of
|
||||
every commit that was reviewed and its status, plus additional
|
||||
comments:
|
||||
|
||||
7e7dcedaf31ee35499875491c9f569c575d28435: Not applied.
|
||||
As its commit message suggests, this commit ported Dino from GTK3 to
|
||||
GTK4, but this fork will keep using GTK3 as a requirement.
|
||||
|
||||
2b3ce5fc95c63ed7d54e207db0585c8b8bbcd603: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
f44cbe02c17df1f02ad49c63cd784fec0ea02d85: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
e51b55432fe98e0fbc036fe785ef50fbf1589034: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
0af92393f134c59646deaab7d963a32c0d3d58b2: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
517363dfc9dab81c4b59310c739bcf71266fabe0: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
6bfa70fc7045669be435d3757456e1da7d341927: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
5103a7fb7b32fcee6656dbae0eec54fb26f89d80: Not applied.
|
||||
The resource tree for Dino got changed during the GTK4 port, including
|
||||
some icons. However, this commit is not needed for this fork.
|
||||
|
||||
7b252d040a845b7e9c515f61927fbf5bf7ed4c3a: Partially applied.
|
||||
However, it had to be slightly adapted to the dependencies as listed
|
||||
by our version of plugins/rtp/CMakeLists.txt.
|
||||
|
||||
d6afa6e8ff4cfb533140d9434b83f18f627f11ca: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
054d3fec1627a9ee8d84d636d578aa68d3494d19: Not applied.
|
||||
None of these changes seem required or seem to fix anything in our
|
||||
fork.
|
||||
|
||||
e85477bb19166e742e34852b2a99b33e55565ded: Not applied.
|
||||
I am unsure whether this commit would break anything, and so far I had
|
||||
no problems on 0.56.0, which is older than 0.56.1.
|
||||
Also, these changes were introduced by the change from GTK3 to GTK4.
|
||||
Some further reference: https://github.com/dino/dino/pull/1234.
|
||||
|
||||
0aa73c4569a90031d8a88173f8689c18d02ddbe2: Not applied.
|
||||
Because of the same reasons as e85477bb19166e742e34852b2a99b33e55565ded.
|
||||
|
||||
14bc3d6717515e0b34b02ef5b5ad4c3ec52ccdc2: Not applied.
|
||||
This is regression introduced by
|
||||
f44cbe02c17df1f02ad49c63cd784fec0ea02d85, which was part of the effort
|
||||
to port Dino to GTK4.
|
||||
|
||||
117f19381233207e4b5aef03c82e7dd4b2d1debd: Not applied.
|
||||
Again, these look like regressions introduced with the migration to
|
||||
GTK4.
|
||||
|
||||
b8e84c83268a11ae41ad1d673999362427fd755c: Applied.
|
||||
|
||||
7ad52d9335579d03613036a7da9967fcf0c5d6b3: Not applied.
|
||||
This commit looks incompatible with GTK3, but I might be wrong.
|
||||
|
||||
146af3152475f12c9b19a92c4779a53f6fc517ce: Not applied.
|
||||
Some resources were moved when porting Dino to GTK4, but this does not
|
||||
apply to our fork.
|
||||
|
||||
21ab48e09aa6b0ade8f25bdc93f89f8d3aa462e7: Not applied.
|
||||
It looks like this is trying to solve a regression introduced during
|
||||
the migration from GTK3 to GTK4, as the calls to
|
||||
jid_entry.key_release_event.connect() and
|
||||
nick_entry.key_release_event.connect() are not commented out on this
|
||||
fork.
|
||||
|
||||
85342ee2eb2aa3e6d7599c503d17c00d861bafcb: Not applied.
|
||||
Drag and drop uploading always forked for this fork.
|
||||
|
||||
03878eee495cac8dcc8baf0ff4f84e4c9e76114e: Not applied.
|
||||
These were disabled during the migration from GTK3 to GTK4, so this
|
||||
commit does not apply to this fork.
|
||||
|
||||
7d8b08deca0aa4eb24def6b9af4ec180c0bc9a27: Partially applied.
|
||||
Whereas the changes on list_row.ui caused many conflicts, the null
|
||||
check on conversation_view_controller.vala is required.
|
||||
|
||||
9c736af765d8c62838440afbfd2ad7ee78b44951: Not applied.
|
||||
check_if_done() was introduced by
|
||||
21ab48e09aa6b0ade8f25bdc93f89f8d3aa462e7, which was not applied,
|
||||
either.
|
||||
|
||||
6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92: Partially applied.
|
||||
Almost everything could be cleanly backported, except from
|
||||
main/src/ui/add_conversation/conference_list.vala, where the original
|
||||
implementation was preferred.
|
||||
|
||||
80258a874ddfeb87b4b71f5791eab94a2465de6d: Not applied.
|
||||
I have no personal interest in XMPP reactions for this fork, as other
|
||||
popular XMPP clients such as Conversations are still rely on styling as
|
||||
described on https://xmpp.org/extensions/xep-0393.html#quote.
|
||||
|
||||
bc5a1d35cbf5c1aca406fa0fe81ca60d6b280bd5: Applied.
|
||||
|
||||
7e0d1db1965555720db2bef7380e61c23ef6dbcd: Applied.
|
||||
|
||||
f82f788f43e385391db2827cde151830fc91bc14: Applied.
|
||||
|
||||
1bf57a42fa5c36977132d21f59ca6637fcd0c3d3: Applied.
|
||||
|
||||
11b6e615b73e4183a06f9d456634c44ead612336: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
09829b33824ab7d1fbf9886b7ed3e42cd8c34ff2: Not applied.
|
||||
This commit fixes a regression introduced by
|
||||
80258a874ddfeb87b4b71f5791eab94a2465de6d, which is related to
|
||||
reactions, something this fork does not implement.
|
||||
|
||||
a45280f8dfe45f8908b44cd13996316af44117e9: Not applied.
|
||||
This commit is related to reactions, which this fork does not
|
||||
implement.
|
||||
|
||||
2ab7374aa53f70b30765a02865d92e6d71c6e623: Not applied.
|
||||
This commit is related to reactions, which this fork does not
|
||||
implement.
|
||||
|
||||
a2f2224781a82121c86a1f19b309245bc8369a91: Applied.
|
||||
|
||||
809c1579e41000f2f43eeb05735afc8165a1a430: Applied.
|
||||
The commit message was a bit misleading: the real intention behind it
|
||||
was to use the same implementation for both libsoup2/3.
|
||||
|
||||
6e37f3fe3fa0f4ce9a25a91e9d97191c8e4abec1: Applied.
|
||||
|
||||
e62955d3cf266a7f7ff0f2085a64f1c99021127c: Not applied.
|
||||
Async uploads and downloads seem to work well. I do not see the need
|
||||
for this patch.
|
||||
|
||||
7a19a25156a73e7e6b6d77fabc7621e7d2c443f0: Partially applied.
|
||||
The changes to libdino/src/service/reactions.vala are out of scope for
|
||||
this fork.
|
||||
|
||||
a2e894dda132f1679ee8e9998879be6bda7ab320: Applied.
|
||||
|
||||
cdd22e404eca3db640b6f2f2789314f7cbb65de6: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
d1fb22ebedca7dbbbd0f693baa3c38d99ab5c344: Not applied.
|
||||
This commit is related to reactions, which this fork does not
|
||||
implement.
|
||||
|
||||
4d50c51a75de70c0f30a196e1f128154ba1651fa: Applied.
|
||||
|
||||
30f99d1347f8f5e2db364a25910d76b0faf2ea36: Applied.
|
||||
|
||||
799d09a4c98d1a00790f261600d3f4d813140954: Applied.
|
||||
|
||||
4d7809bb12199a598b531ca3ca019a4bb5a867f7: Partially applied.
|
||||
- Too many changes are introduced to
|
||||
main/src/ui/chat_input/encryption_button.vala, which might break this
|
||||
fork.
|
||||
- Changes related to own_occupant_ids are
|
||||
related to reactions (as introduced by
|
||||
80258a874ddfeb87b4b71f5791eab94a2465de6d), and thus are out of scope
|
||||
for this fork.
|
||||
- This commit removes plugins/omemo/src/ui/manage_key_dialog.vala, which
|
||||
this fork still relies on.
|
||||
- headerbar is still used by plugins/omemo/src/ui/manage_key_dialog.vala,
|
||||
and thus should not be removed.
|
||||
- Too many changes are introduced to
|
||||
main/src/ui/conversation_content_view/conversation_item_skeleton.vala,
|
||||
which might break this fork.
|
||||
- The changes in
|
||||
main/src/ui/conversation_content_view/conversation_view.vala break
|
||||
the build.
|
||||
- The changes in
|
||||
main/src/ui/conversation_view_controller.vala break the build.
|
||||
|
||||
dc52e7595cca06d0a2da7d11b3c88cb2f7ce529c: Not applied.
|
||||
I am not interested in XEP-0461 (replies) for this fork, as the
|
||||
fallback implementations is already provided here.
|
||||
|
||||
60371331e0882758b0b9c2efedb3821e716defd7: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
424a4290622246303a7b73410d7e4a5a6d57dd6b: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
0c4aea96ffbc05d6efeb9a83424b872ce7f30d88: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
cb3b19b01deb8460627578b885339e7528411f6f: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
75500dc767f2cf657c0fbb5d2a4d4557183ed2e9: Not applied.
|
||||
This commit breaks the build in some places, and I am not particulary
|
||||
interested in conversation pinning anyway.
|
||||
|
||||
860c72bfc93d252d45eb97e71cf9ff22985c7ef9: Not applied.
|
||||
This commit fixes regressions introduced by other commits that were not
|
||||
introduced by this fork, such as
|
||||
7e7dcedaf31ee35499875491c9f569c575d28435.
|
||||
|
||||
73c0263f35a73b68d20d299ee7fe8c37b9a6ffeb: Not applied.
|
||||
This commit depends on other commits that were not introduced by this
|
||||
fork, such as cb3b19b01deb8460627578b885339e7528411f6f, which was
|
||||
related to replies (not implemented by this fork).
|
||||
|
||||
05289e0b4dc9bc076955e27b30b386cb7f0604c7: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
7da79864b384c9370a5937d480230e771834d91a: Not applied.
|
||||
Conversation pinning is out of scope for this fork.
|
||||
|
||||
7e0d1db1965555720db2bef7380e61c23ef6dbcd: Applied.
|
||||
|
||||
f6e73d85c00a60a719da95a048ba2c15712325c3: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
1ef42b47d22d21600ccf1e2d8b4d80605448660d: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
2741bf21ae6d53324a512dacef65d540be840fe4: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
ba9462503c0561dbe8306e3bf6aa49392bfc8078: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
04acab82c98f5d9cdb798ba3baac8d73b097b1df: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
e934a76a1139938ae668836b812102cd5d9c9d9f: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
Also, this fork already has a back button for ConversationTitleBar.
|
||||
|
||||
0d7c8bb6e117f8cdd631730302413aad21632c2d: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
92aca5672db723121471e513e83b68742761d1b5: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
4b391f3f31c2272be11a24c8301641b045260e99: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
ef98adb18a016dba65162602eb336fb82c64805e: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
6a182ba313026b93d54a9d2246a0ab68894c6833: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
99d9cb383abb1a33f6d0572deb4292dbf358f3ce: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
cc7db3b85f7b29bfac333937d8bf09a81d8dc4a5: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
e35df88d4a00c3a34f2b4d9fb7f10bb5d877bd29: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
26be9d4bb40b223cb8a657b03e6457988a8cc269: Not applied.
|
||||
Reactions are out of scope for this fork.
|
||||
|
||||
5d9978b38bb0e729dcccecddd08cc59e5585a6cc: Not applied.
|
||||
Reactions are out of scope for this fork.
|
||||
|
||||
c813a6d2405980c71450cefa10abdf11fab8e995: Not applied.
|
||||
GTK4-related changes are out of scope for this fork.
|
||||
|
||||
e833a924b5a66048506b4e0b0885ce2e35cac6fc: Applied.
|
||||
|
||||
e833a924b5a66048506b4e0b0885ce2e35cac6fc: Applied.
|
||||
|
||||
04eb0e763b0e02ea1bc698f1ccb24f84b0154010: Not applied.
|
||||
This commit is way too big to merge and is tighly coupled to upstream
|
||||
source code. Maybe a similar commit can be provided independently.
|
||||
|
||||
d0a00e1e7549609f7d83e7b432f7f547a1fcebb0: Applied.
|
||||
|
||||
1dbacbbcab139d0f8036446441ad143ef7e7eb30: Applied.
|
||||
|
||||
10a2bce5122dcd1e6fef037633a26568bf27d4d1: Applied.
|
||||
|
||||
b0b81b88c6948dcfd2b1b82a9fe7357316a3af1f: Not applied.
|
||||
Both reactions and replies are out of scope for this fork.
|
||||
|
||||
e3c833bce0713e9a0290841306c7727dfc1e3860: Applied.
|
||||
This fork defines different logic for update_received_mark(), so the
|
||||
commit was adapted to it.
|
||||
|
||||
1e23b7bbd2a66e5ff40ae5fc5aa6523fa604f242: Not applied.
|
||||
Replies are out of scope for this fork.
|
||||
|
||||
9e11bef219880b5bdc5d299c31ec6249596a86ba: Not applied.
|
||||
string_if_tooltips_active() was added by
|
||||
6bfa70fc7045669be435d3757456e1da7d341927, which is GTK4-related.
|
||||
|
||||
921f28c84bbda56ec93df7dcde7c828eaabb0b58: Not applied.
|
||||
Reactions are out of scope for this fork.
|
||||
|
||||
3aa3912dc3ea740a5b93f8b694ead45e1b655238: Not applied.
|
||||
main/src/ui/widgets/date_separator.vala depends on GTK4 libadwaita.
|
||||
|
||||
e0ece2aa62aa6b8350be83c787ea003a75f07437: Partially applied.
|
||||
The changes to main/data/unified_main_content.ui and
|
||||
main/src/ui/conversation_selector/conversation_selector.vala depend on
|
||||
other commits related to GTK4 and libadwaita.
|
||||
|
||||
95fefaff51e5506d3f0e5fe8bced14aeb3fbe037: Applied.
|
||||
|
||||
18321ed15ce782ff5d1f24de9f2fb459d714d125: Partially applied.
|
||||
The changes to libdino/src/service/reactions.vala and
|
||||
xmpp-vala/src/module/xep/0444_reactions.vala are not relevant to this
|
||||
fork.
|
||||
|
||||
d76e12b215eb62e4eda5a0f92fbf5c1bd7c1848e: Applied.
|
||||
|
||||
f74c1f18b12df0d650f74b6fa43b7f2f0a9bce79: Applied.
|
||||
|
||||
1d123c7e66d963fd8cc8cc4250b5813a62676f56: Applied.
|
||||
|
||||
d092473fe401a5a668e57f054efbd1e84ac6ca59: Applied.
|
||||
|
||||
1559a7a60370c2aa0203e5c4222def4ae3258006: Applied.
|
||||
|
||||
32ae87a3c4cebaa05e0e702d744900cd414000db: Not applied.
|
||||
Seems to cause many conflicts while providing little benefit.
|
||||
|
||||
116682e311edca6665a0497c8b225b4fe69859a7: Partially applied.
|
||||
Changes to main/src/ui/conversation_content_view/quote_widget.vala are
|
||||
not relevant to this fork.
|
||||
|
||||
9c5e36020d8997452d4fd07c5a153e1e7fc24088: Applied.
|
||||
|
||||
e73b556a1ae5ea4af41c610f7b05545cf60d59c4: Not applied.
|
||||
This commit is way too big to merge and is tighly coupled to upstream
|
||||
source code. Maybe a similar commit can be provided independently.
|
||||
|
||||
5568bbc6bf505c4f8ea93fc460dbeff6f4d36e15: Partially applied.
|
||||
Changes related to libadwaita are out of scope for this fork.
|
15
README.md
15
README.md
|
@ -1,10 +1,21 @@
|
|||
# An unbranded Dino fork
|
||||
|
||||
## Screenshots
|
||||
|
||||
![Screenshot showing mobile form factor and OMEMO-by-default support](doc/screenshot-2.png)
|
||||
|
||||
![Screenshot showing a MUC room in desktip form factor](doc/screenshot-1.png)
|
||||
|
||||
## Features
|
||||
|
||||
- **OMEMO encryption set by default**.
|
||||
- Portrait screen support with `libhandy`.
|
||||
- Improved [`connection_manager.vala`](libdino/src/service/connection_manager.vala).
|
||||
- Quote messages according to XEP-0393 Message Styling.
|
||||
- Copy messages.
|
||||
- Optional send button and Enter-to-send accelerator.
|
||||
- Keeps using GTK3.
|
||||
- Includes some features that did not make into upstream.
|
||||
- Backports bugfixes and improvements from upstream.
|
||||
- Backports bugfixes and improvements from upstream (see [`BACKPORT`](BACKPORT)).
|
||||
|
||||
## TODO
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
find_package(Nice QUIET)
|
||||
if (Nice_FOUND AND NOT SOUP_VERSION AND NOT USE_SOUP3)
|
||||
if (Nice_FOUND)
|
||||
file(GET_RUNTIME_DEPENDENCIES
|
||||
RESOLVED_DEPENDENCIES_VAR Nice_DEPENDENCIES
|
||||
UNRESOLVED_DEPENDENCIES_VAR Nice_UNRESOLVED_DEPENDENCIES
|
||||
|
@ -9,11 +9,21 @@ if (Nice_FOUND AND NOT SOUP_VERSION AND NOT USE_SOUP3)
|
|||
)
|
||||
foreach (lib ${Nice_DEPENDENCIES})
|
||||
if (lib MATCHES ".*/libsoup-3.*")
|
||||
if(SOUP_VERSION AND NOT SOUP_VERSION EQUAL 3)
|
||||
message(FATAL_ERROR "libnice-${Nice_VERSION} depends on "
|
||||
"libsoup-3, but SOUP_VERSION=${SOUP_VERSION} was given.")
|
||||
endif()
|
||||
|
||||
set(SOUP_VERSION 3)
|
||||
endif ()
|
||||
endforeach ()
|
||||
foreach (lib ${Nice_DEPENDENCIES})
|
||||
if (lib MATCHES ".*/libsoup-2.*")
|
||||
if(SOUP_VERSION AND NOT SOUP_VERSION EQUAL 2)
|
||||
message(FATAL_ERROR "libnice-${Nice_VERSION} depends on "
|
||||
"libsoup-2, but SOUP_VERSION=${SOUP_VERSION} was given.")
|
||||
endif()
|
||||
|
||||
set(SOUP_VERSION 2)
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
@ -24,7 +34,7 @@ elseif (NOT SOUP_VERSION)
|
|||
find_package(Soup2 QUIET)
|
||||
find_package(Soup3 QUIET)
|
||||
# Only use libsoup 3 if specifically requested or when libsoup 2 is not available
|
||||
if (Soup3_FOUND AND NOT Soup2_FOUND OR USE_SOUP3)
|
||||
if (Soup3_FOUND AND NOT Soup2_FOUND)
|
||||
set(SOUP_VERSION 3)
|
||||
else ()
|
||||
set(SOUP_VERSION 2)
|
||||
|
|
|
@ -22,7 +22,7 @@ DISABLE_FAST_VAPI=
|
|||
LIB_SUFFIX=
|
||||
NO_DEBUG=
|
||||
FETCH_ONLY=
|
||||
USE_SOUP3=
|
||||
SOUP_VERSION=2
|
||||
|
||||
EXEC_PREFIX=
|
||||
BINDIR=
|
||||
|
@ -113,7 +113,7 @@ while true; do
|
|||
--valac-flags ) VALAC_FLAGS="$2"; shift; shift ;;
|
||||
--lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;;
|
||||
--with-libsignal-in-tree ) BUILD_LIBSIGNAL_IN_TREE=yes; shift ;;
|
||||
--with-libsoup3 ) USE_SOUP3=yes; shift ;;
|
||||
--with-libsoup3 ) SOUP_VERSION=3; shift ;;
|
||||
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
|
||||
--no-debug ) NO_DEBUG=yes; shift ;;
|
||||
--fetch-only ) FETCH_ONLY=yes; shift ;;
|
||||
|
@ -259,7 +259,7 @@ cmake -G "$cmake_type" \
|
|||
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
|
||||
-DBUILD_TESTS="$BUILD_TESTS" \
|
||||
-DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \
|
||||
-DUSE_SOUP3="$USE_SOUP3" \
|
||||
-DSOUP_VERSION="$SOUP_VERSION" \
|
||||
-DVALA_EXECUTABLE="$VALAC" \
|
||||
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
|
||||
-DDISABLE_FAST_VAPI="$DISABLE_FAST_VAPI" \
|
||||
|
|
|
@ -20,6 +20,12 @@ sudo zypper install cmake gcc-c++ gpgme-devel libnotify-devel libgcrypt-devel pk
|
|||
sudo pacman -S cmake vala ninja glib2 glib-networking gtk3 gpgme libgee>=0.10 libgcrypt libsoup sqlite qrencode gspell gstreamer gst-plugins-base gst-plugins-good gst-plugin-gtk webrtc-audio-processing libnice libsrtp libsignal-protocol-c
|
||||
```
|
||||
|
||||
### Alpine Linux
|
||||
|
||||
```
|
||||
sudo apk add git make cmake gcc g++ vala libgee-dev sqlite-dev gst-plugins-good gtk4.0-dev libadwaita-dev libgcrypt-dev libsrtp-dev libsoup-dev libnice-dev musl-utils libqrencode-dev libsignal-protocol-c-dev gpgme-dev gstreamer-dev gst-plugins-base-dev
|
||||
```
|
||||
|
||||
# libsignal-protocol-c
|
||||
If build complains about missing or incompatible libsignal-protocol-c and it is not provided by your distribution, you can fetch and build it in tree by using `./configure --with-libsignal-in-tree`.
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -6,8 +6,15 @@ find_packages(LIBDINO_PACKAGES REQUIRED
|
|||
GObject
|
||||
)
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_LIST_DIR}/version.vala
|
||||
"namespace Dino {
|
||||
public const string VERSION = \"v${PROJECT_VERSION}\";
|
||||
}"
|
||||
)
|
||||
|
||||
vala_precompile(LIBDINO_VALA_C
|
||||
SOURCES
|
||||
version.vala
|
||||
src/application.vala
|
||||
|
||||
src/dbus/login1.vala
|
||||
|
@ -90,7 +97,7 @@ DEPENDS
|
|||
${CMAKE_BINARY_DIR}/exports/dino_i18n.h
|
||||
)
|
||||
|
||||
add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" -DDINO_VERSION=\"${PROJECT_VERSION}\")
|
||||
add_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)
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
# version_vala
|
||||
dot_git = meson.current_source_dir() / '../.git'
|
||||
version_file = meson.current_source_dir() / '../VERSION'
|
||||
command = [prog_python, files('version.py'), version_file, '@OUTPUT@', '--git-repo', meson.current_source_dir()]
|
||||
if prog_git.found()
|
||||
command += ['--git', prog_git]
|
||||
endif
|
||||
depend_files = []
|
||||
if fs.exists(dot_git)
|
||||
depend_files += [dot_git]
|
||||
endif
|
||||
if fs.exists(version_file)
|
||||
depend_files += [version_file]
|
||||
endif
|
||||
version_vala = custom_target('libdino_version_vala', command: command, output: 'version.vala', depend_files: depend_files)
|
||||
|
||||
# libdino
|
||||
dependencies = [
|
||||
dep_gdk_pixbuf,
|
||||
dep_gee,
|
||||
dep_gio,
|
||||
dep_glib,
|
||||
dep_gmodule,
|
||||
dep_qlite,
|
||||
dep_xmpp_vala
|
||||
]
|
||||
sources = files(
|
||||
'src/application.vala',
|
||||
'src/dbus/login1.vala',
|
||||
'src/dbus/notifications.vala',
|
||||
'src/dbus/upower.vala',
|
||||
'src/entity/account.vala',
|
||||
'src/entity/call.vala',
|
||||
'src/entity/conversation.vala',
|
||||
'src/entity/encryption.vala',
|
||||
'src/entity/file_transfer.vala',
|
||||
'src/entity/message.vala',
|
||||
'src/entity/settings.vala',
|
||||
'src/plugin/interfaces.vala',
|
||||
'src/plugin/loader.vala',
|
||||
'src/plugin/registry.vala',
|
||||
'src/service/avatar_manager.vala',
|
||||
'src/service/blocking_manager.vala',
|
||||
'src/service/call_store.vala',
|
||||
'src/service/call_state.vala',
|
||||
'src/service/call_peer_state.vala',
|
||||
'src/service/calls.vala',
|
||||
'src/service/chat_interaction.vala',
|
||||
'src/service/connection_manager.vala',
|
||||
'src/service/content_item_store.vala',
|
||||
'src/service/conversation_manager.vala',
|
||||
'src/service/counterpart_interaction_manager.vala',
|
||||
'src/service/database.vala',
|
||||
'src/service/entity_capabilities_storage.vala',
|
||||
'src/service/entity_info.vala',
|
||||
'src/service/file_manager.vala',
|
||||
'src/service/file_transfer_storage.vala',
|
||||
'src/service/jingle_file_transfers.vala',
|
||||
'src/service/message_correction.vala',
|
||||
'src/service/message_processor.vala',
|
||||
'src/service/message_storage.vala',
|
||||
'src/service/module_manager.vala',
|
||||
'src/service/muc_manager.vala',
|
||||
'src/service/notification_events.vala',
|
||||
'src/service/presence_manager.vala',
|
||||
'src/service/registration.vala',
|
||||
'src/service/roster_manager.vala',
|
||||
'src/service/search_processor.vala',
|
||||
'src/service/stream_interactor.vala',
|
||||
'src/service/util.vala',
|
||||
'src/util/display_name.vala',
|
||||
'src/util/util.vala',
|
||||
'src/util/weak_map.vala',
|
||||
)
|
||||
sources += [version_vala]
|
||||
c_args = [
|
||||
'-DDINO_SYSTEM_LIBDIR_NAME="@0@"'.format(get_option('prefix') / get_option('libdir')),
|
||||
'-DDINO_SYSTEM_PLUGIN_DIR="@0@"'.format(get_option('prefix') / get_option('plugindir')),
|
||||
'-DG_LOG_DOMAIN="libdino"',
|
||||
]
|
||||
lib_dino = library('dino', sources, c_args: c_args, include_directories: include_directories('src'), dependencies: dependencies)
|
||||
dep_dino = declare_dependency(link_with: lib_dino, include_directories: include_directories('.', 'src'))
|
|
@ -2,7 +2,6 @@ using Dino.Entities;
|
|||
|
||||
namespace Dino {
|
||||
|
||||
extern const string VERSION;
|
||||
public string get_version() { return VERSION; }
|
||||
public string get_short_version() {
|
||||
if (!VERSION.contains("~")) return VERSION;
|
||||
|
@ -16,6 +15,8 @@ public interface Application : GLib.Application {
|
|||
public abstract StreamInteractor stream_interactor { get; set; }
|
||||
public abstract Plugins.Registry plugin_registry { get; set; }
|
||||
public abstract SearchPathGenerator? search_path_generator { get; set; }
|
||||
public abstract uint inhibit_app(string reason);
|
||||
public abstract void uninhibit_app(uint cookie);
|
||||
|
||||
internal static string print_xmpp;
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ public class FileTransfer : Object {
|
|||
public State state { get; set; default=State.NOT_STARTED; }
|
||||
public int provider { get; set; }
|
||||
public string info { get; set; }
|
||||
public uint64 transferred_bytes {get; set; }
|
||||
public Cancellable cancellable { get; default=new Cancellable(); }
|
||||
|
||||
private Database? db;
|
||||
|
|
|
@ -13,6 +13,9 @@ public class Settings : Object {
|
|||
convert_utf8_smileys_ = col_to_bool_or_default("convert_utf8_smileys", true);
|
||||
check_spelling = col_to_bool_or_default("check_spelling", true);
|
||||
default_encryption = col_to_encryption_or_default("default_encryption", Encryption.OMEMO);
|
||||
send_button = col_to_bool_or_default("send_button", false);
|
||||
enter_newline = col_to_bool_or_default("enter_newline", false);
|
||||
quit_ctrl_q = col_to_bool_or_default("quit_ctrl_q", true);
|
||||
}
|
||||
|
||||
private bool col_to_bool_or_default(string key, bool def) {
|
||||
|
@ -98,6 +101,46 @@ public class Settings : Object {
|
|||
default_encryption_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
public signal void send_button_update(bool visible);
|
||||
private bool send_button_;
|
||||
public bool send_button {
|
||||
get { return send_button_; }
|
||||
set {
|
||||
db.settings.upsert()
|
||||
.value(db.settings.key, "send_button", true)
|
||||
.value(db.settings.value, value.to_string())
|
||||
.perform();
|
||||
send_button_ = value;
|
||||
send_button_update(value);
|
||||
}
|
||||
}
|
||||
|
||||
private bool enter_newline_;
|
||||
public bool enter_newline {
|
||||
get { return enter_newline_; }
|
||||
set {
|
||||
db.settings.upsert()
|
||||
.value(db.settings.key, "enter_newline", true)
|
||||
.value(db.settings.value, value.to_string())
|
||||
.perform();
|
||||
enter_newline_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
public signal void quit_ctrl_q_update(bool active);
|
||||
private bool quit_ctrl_q_;
|
||||
public bool quit_ctrl_q {
|
||||
get { return quit_ctrl_q_; }
|
||||
set {
|
||||
db.settings.upsert()
|
||||
.value(db.settings.key, "quit_ctrl_q", true)
|
||||
.value(db.settings.value, value.to_string())
|
||||
.perform();
|
||||
quit_ctrl_q_ = value;
|
||||
quit_ctrl_q_update(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@ using Gee;
|
|||
namespace Dino.Plugins {
|
||||
|
||||
public class Registry {
|
||||
internal ArrayList<EncryptionListEntry> encryption_list_entries = new ArrayList<EncryptionListEntry>();
|
||||
internal HashMap<string, CallEncryptionEntry> call_encryption_entries = new HashMap<string, CallEncryptionEntry>();
|
||||
internal ArrayList<AccountSettingsEntry> account_settings_entries = new ArrayList<AccountSettingsEntry>();
|
||||
internal ArrayList<ContactDetailsProvider> contact_details_entries = new ArrayList<ContactDetailsProvider>();
|
||||
internal Map<string, TextCommand> text_commands = new HashMap<string, TextCommand>();
|
||||
internal Gee.List<ConversationAdditionPopulator> conversation_addition_populators = new ArrayList<ConversationAdditionPopulator>();
|
||||
internal Gee.List<NotificationPopulator> notification_populators = new ArrayList<NotificationPopulator>();
|
||||
internal Gee.Collection<ConversationTitlebarEntry> conversation_titlebar_entries = new Gee.TreeSet<ConversationTitlebarEntry>((a, b) => {
|
||||
public ArrayList<EncryptionListEntry> encryption_list_entries = new ArrayList<EncryptionListEntry>();
|
||||
public HashMap<string, CallEncryptionEntry> call_encryption_entries = new HashMap<string, CallEncryptionEntry>();
|
||||
public ArrayList<AccountSettingsEntry> account_settings_entries = new ArrayList<AccountSettingsEntry>();
|
||||
public ArrayList<ContactDetailsProvider> contact_details_entries = new ArrayList<ContactDetailsProvider>();
|
||||
public Map<string, TextCommand> text_commands = new HashMap<string, TextCommand>();
|
||||
public Gee.List<ConversationAdditionPopulator> conversation_addition_populators = new ArrayList<ConversationAdditionPopulator>();
|
||||
public Gee.List<NotificationPopulator> notification_populators = new ArrayList<NotificationPopulator>();
|
||||
public Gee.Collection<ConversationTitlebarEntry> conversation_titlebar_entries = new Gee.TreeSet<ConversationTitlebarEntry>((a, b) => {
|
||||
return (int)(a.order - b.order);
|
||||
});
|
||||
public VideoCallPlugin? video_call_plugin;
|
||||
|
|
|
@ -144,7 +144,7 @@ public class AvatarManager : StreamInteractionModule, Object {
|
|||
}
|
||||
uint8[] buffer;
|
||||
pixbuf.save_to_buffer(out buffer, "png");
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) {
|
||||
Xmpp.Xep.UserAvatars.publish_png(stream, buffer, pixbuf.width, pixbuf.height);
|
||||
}
|
||||
|
|
|
@ -21,22 +21,26 @@ public class BlockingManager : StreamInteractionModule, Object {
|
|||
}
|
||||
|
||||
public bool is_blocked(Account account, Jid jid) {
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_blocked(stream, jid.to_string());
|
||||
}
|
||||
|
||||
public void block(Account account, Jid jid) {
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).block(stream, { jid.to_string() });
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) {
|
||||
stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).block(stream, { jid.to_string() });
|
||||
}
|
||||
}
|
||||
|
||||
public void unblock(Account account, Jid jid) {
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).unblock(stream, { jid.to_string() });
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) {
|
||||
stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).unblock(stream, { jid.to_string() });
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_supported(Account account) {
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_supported(stream);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ public class Dino.CallState : Object {
|
|||
call.state = Call.State.DECLINED;
|
||||
|
||||
if (use_cim) {
|
||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||
XmppStream? stream = stream_interactor.get_stream(call.account);
|
||||
if (stream == null) return;
|
||||
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_reject(stream, cim_counterpart, cim_call_id, cim_message_type);
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ public class Dino.CallState : Object {
|
|||
peers_cpy.add_all(peers.values);
|
||||
|
||||
if (group_call != null) {
|
||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||
XmppStream? stream = stream_interactor.get_stream(call.account);
|
||||
if (stream != null) {
|
||||
stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, group_call.muc_jid);
|
||||
}
|
||||
|
|
|
@ -56,15 +56,15 @@ public class ConnectionManager : Object {
|
|||
public bool acked;
|
||||
|
||||
public Connection() {
|
||||
reset();
|
||||
reset.begin();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
public async void reset() {
|
||||
acked = false;
|
||||
if (stream != null) {
|
||||
stream.detach_modules();
|
||||
|
||||
stream.disconnect.begin();
|
||||
yield stream.disconnect();
|
||||
}
|
||||
stream = null;
|
||||
uuid = Xmpp.random_uuid();
|
||||
|
@ -252,11 +252,32 @@ public class ConnectionManager : Object {
|
|||
}
|
||||
}
|
||||
|
||||
private void check_ping_reconnect(Xmpp.Xep.Ping.Module identity,
|
||||
XmppStream stream, Account account) {
|
||||
identity.send_ping.begin(stream, account.bare_jid.domain_jid, () => {
|
||||
if (connections[account].stream != stream) return;
|
||||
connections[account].acked = true;
|
||||
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 (connections[account].acked) return false;
|
||||
|
||||
// Reconnect. Nothing gets through the stream.
|
||||
debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream);
|
||||
change_connection_state(account, ConnectionState.DISCONNECTED);
|
||||
|
||||
connections[account].reset();
|
||||
connect_stream.begin(account);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void check_reconnect(Account account, bool directly_reconnect = false) {
|
||||
if (!connections.has_key(account)) return;
|
||||
|
||||
XmppStream? stream = connections[account].stream;
|
||||
|
||||
var cancellable = new Cancellable();
|
||||
debug(@"account.domainpart=$(account.domainpart)");
|
||||
|
||||
|
@ -267,29 +288,23 @@ public class ConnectionManager : Object {
|
|||
debug(@"can-reach: $(reachable)");
|
||||
|
||||
if (reachable) {
|
||||
Xmpp.Xep.Ping.Module? identity = null;
|
||||
if (connections[account].connection_state == ConnectionState.CONNECTING) return;
|
||||
|
||||
XmppStream? stream = connections[account].stream;
|
||||
connections[account].acked = false;
|
||||
change_connection_state(account, ConnectionState.CONNECTING);
|
||||
|
||||
stream.get_module(Xep.Ping.Module.IDENTITY).send_ping.begin(stream, account.bare_jid.domain_jid, () => {
|
||||
connections[account].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 (connections[account].acked) return false;
|
||||
|
||||
// Reconnect. Nothing gets through the stream.
|
||||
debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream);
|
||||
if (stream != null
|
||||
&& (identity = stream.get_module(Xep.Ping.Module.IDENTITY)) != null) {
|
||||
change_connection_state(account, ConnectionState.CONNECTING);
|
||||
check_ping_reconnect(identity, stream, account);
|
||||
}
|
||||
else {
|
||||
change_connection_state(account, ConnectionState.DISCONNECTED);
|
||||
|
||||
connections[account].reset();
|
||||
connect_stream.begin(account);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Error e) {
|
||||
|
|
|
@ -120,6 +120,7 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
}
|
||||
|
||||
yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta);
|
||||
file_transfer.state = FileTransfer.State.COMPLETE;
|
||||
|
||||
} catch (Error e) {
|
||||
warning("Send file error: %s", e.message);
|
||||
|
@ -210,6 +211,8 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
|
||||
private async void download_file_internal(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) {
|
||||
try {
|
||||
file_transfer.cancellable.reset();
|
||||
|
||||
// Get meta info
|
||||
FileReceiveData receive_data = file_provider.get_file_receive_data(file_transfer);
|
||||
FileDecryptor? file_decryptor = null;
|
||||
|
@ -246,14 +249,20 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
|
||||
|
||||
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
|
||||
file_transfer.cancellable.reset();
|
||||
|
||||
uint inhibit_cookie = Application.get_default()
|
||||
.inhibit_app("Ongoing file download");
|
||||
uint8[] buffer = new uint8[1024];
|
||||
ssize_t read;
|
||||
while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) {
|
||||
buffer.length = (int) read;
|
||||
file_transfer.transferred_bytes += (uint64)read;
|
||||
yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable);
|
||||
buffer.length = 1024;
|
||||
}
|
||||
yield input_stream.close_async(Priority.LOW, file_transfer.cancellable);
|
||||
Application.get_default().uninhibit_app(inhibit_cookie);
|
||||
yield os.close_async(Priority.LOW, file_transfer.cancellable);
|
||||
file_transfer.path = file.get_basename();
|
||||
file_transfer.input_stream = yield file.read_async();
|
||||
|
@ -285,6 +294,7 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
file_transfer.file_name = file_meta.file_name;
|
||||
file_transfer.size = (int)file_meta.size;
|
||||
file_transfer.info = info;
|
||||
file_transfer.transferred_bytes = 0;
|
||||
|
||||
var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta);
|
||||
if (encryption != Encryption.NONE) file_transfer.encryption = encryption;
|
||||
|
@ -304,11 +314,6 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
warning("Error downloading file: %s", e.message);
|
||||
file_transfer.state = FileTransfer.State.FAILED;
|
||||
}
|
||||
if (file_transfer.size >= 0 && file_transfer.size < 5000000) {
|
||||
download_file_internal.begin(file_provider, file_transfer, conversation, (_, res) => {
|
||||
download_file_internal.end(res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
conversation.last_active = file_transfer.time;
|
||||
|
@ -321,7 +326,7 @@ public class FileManager : StreamInteractionModule, Object {
|
|||
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.state = FileTransfer.State.IN_PROGRESS;
|
||||
file_transfer.path = filename;
|
||||
file_transfer.input_stream = yield file.read_async();
|
||||
} catch (Error e) {
|
||||
|
|
|
@ -366,7 +366,10 @@ public class Dino.HistorySync {
|
|||
* prev_page_result: null if this is the first page request
|
||||
**/
|
||||
private async PageRequestResult get_mam_page(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, PageRequestResult? prev_page_result, Cancellable? cancellable = null) {
|
||||
XmppStream stream = stream_interactor.get_stream(account);
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream == null) {
|
||||
return new PageRequestResult.with_result(PageResult.Error);
|
||||
}
|
||||
Xmpp.MessageArchiveManagement.QueryResult query_result = null;
|
||||
if (prev_page_result == null) {
|
||||
query_result = yield Xmpp.MessageArchiveManagement.V2.query_archive(stream, query_params, cancellable);
|
||||
|
@ -537,7 +540,6 @@ public class Dino.HistorySync {
|
|||
if (to_delete.contains(range1)) continue;
|
||||
|
||||
foreach (MamRange range2 in ranges[server_jid]) {
|
||||
debug("[%s | %s] | %s - %s vs %s - %s", account.bare_jid.to_string(), server_jid.to_string(), range1.from_time.to_string(), range1.to_time.to_string(), range2.from_time.to_string(), range2.to_time.to_string());
|
||||
if (range1 == range2 || to_delete.contains(range2)) continue;
|
||||
|
||||
// Check if range2 is a subset of range1
|
||||
|
@ -593,5 +595,9 @@ public class Dino.HistorySync {
|
|||
this.query_result = query_result;
|
||||
this.stanzas = stanzas;
|
||||
}
|
||||
|
||||
public PageRequestResult.with_result(PageResult page_result) {
|
||||
this.page_result = page_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,7 +220,19 @@ public class JingleFileSender : FileSender, Object {
|
|||
}
|
||||
}
|
||||
try {
|
||||
yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream(stream, full_jid, file_transfer.input_stream, file_transfer.server_file_name, file_meta.size, precondition_name, precondition_options);
|
||||
var? module = stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY);
|
||||
|
||||
if (module == null)
|
||||
throw new FileSendError.UPLOAD_FAILED("unexpected null module");
|
||||
|
||||
module.transferred_bytes.connect((bytes) => {
|
||||
file_transfer.transferred_bytes += bytes;
|
||||
});
|
||||
|
||||
yield module.offer_file_stream(stream, full_jid,
|
||||
file_transfer.cancellable, file_transfer.input_stream,
|
||||
file_transfer.server_file_name, file_meta.size,
|
||||
precondition_name, precondition_options);
|
||||
} catch (Error e) {
|
||||
throw new FileSendError.UPLOAD_FAILED(@"offer_file_stream failed: $(e.message)");
|
||||
}
|
||||
|
|
|
@ -96,7 +96,8 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
|||
}
|
||||
break;
|
||||
case CallItem.TYPE:
|
||||
// handled in `on_call_incoming`
|
||||
// Also handled in `on_call_incoming`
|
||||
notify_content_item(item, conversation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import argparse
|
||||
import subprocess
|
||||
VERSION_VALA = """\
|
||||
namespace Dino {{
|
||||
|
||||
public const string VERSION = "{}";
|
||||
|
||||
}}
|
||||
"""
|
||||
|
||||
def compute_version(file, git_repo, git):
|
||||
try:
|
||||
with open(file) as f:
|
||||
return f.read().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return subprocess.check_output([git, "describe", "--tags"], cwd=git_repo, text=True).strip()
|
||||
|
||||
def generate_version_vala(version):
|
||||
if "\\" in version or "\"" in version:
|
||||
raise ValueError(f"invalid version {version!r}")
|
||||
return VERSION_VALA.format(version)
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Compute the Dino version")
|
||||
p.add_argument("--git-repo", help="Path to checked out git repository")
|
||||
p.add_argument("--git", help="Path to git executable", default="git")
|
||||
p.add_argument("version_file", metavar="VERSION_FILE", help="Use this file's contents as version if the file exists")
|
||||
p.add_argument("output", metavar="OUTPUT", help="Vala file to output to")
|
||||
args = p.parse_args()
|
||||
out = generate_version_vala(compute_version(args.version_file, args.git_repo, args.git))
|
||||
with open(args.output, "w") as f:
|
||||
f.write(out)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -214,7 +214,7 @@ OPTIONS
|
|||
${MAIN_EXTRA_OPTIONS}
|
||||
)
|
||||
|
||||
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DDINO_VERSION=\"${PROJECT_VERSION}\")
|
||||
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\")
|
||||
add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET} src/emojichooser.c)
|
||||
add_dependencies(dino ${GETTEXT_PACKAGE}-translations)
|
||||
target_include_directories(dino PRIVATE src)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="propagate_natural_height">True</property>
|
||||
<property name="max_content_height">500</property>
|
||||
<property name="max_content_height">128</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="visible">True</property>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><gresources><gresource prefix="/im/dino/Dino"><file>icons/dino-account-plus-symbolic.svg</file><file>icons/dino-changes-allowed-symbolic.svg</file><file>icons/dino-changes-prevent-symbolic.svg</file><file>icons/dino-conversation-list-placeholder-arrow.svg</file><file>icons/dino-double-tick-symbolic.svg</file><file>icons/dino-emoticon-symbolic.svg</file><file>icons/dino-qr-code-symbolic.svg</file><file>icons/dino-security-high-symbolic.svg</file><file>icons/dino-microphone-off-symbolic.svg</file><file>icons/dino-microphone-symbolic.svg</file><file>icons/dino-party-popper-symbolic.svg</file><file>icons/dino-phone-hangup-symbolic.svg</file><file>icons/dino-phone-in-talk-symbolic.svg</file><file>icons/dino-phone-missed-symbolic.svg</file><file>icons/dino-phone-ring-symbolic.svg</file><file>icons/dino-phone-symbolic.svg</file><file>icons/dino-status-away.svg</file><file>icons/dino-status-chat.svg</file><file>icons/dino-status-dnd.svg</file><file>icons/dino-status-online.svg</file><file>icons/im.dino.Dino.svg</file><file>icons/im.dino.Dino-symbolic.svg</file><file>icons/dino-tick-symbolic.svg</file><file>icons/dino-video-off-symbolic.svg</file><file>icons/dino-video-symbolic.svg</file><file>icons/dino-device-desktop-symbolic.svg</file><file>icons/dino-device-phone-symbolic.svg</file><file>icons/dino-file-document-symbolic.svg</file><file>icons/dino-file-download-symbolic.svg</file><file>icons/dino-file-image-symbolic.svg</file><file>icons/dino-file-music-symbolic.svg</file><file>icons/dino-file-symbolic.svg</file><file>icons/dino-file-table-symbolic.svg</file><file>icons/dino-file-video-symbolic.svg</file><file>add_conversation/add_contact_dialog.ui</file><file>add_conversation/add_groupchat_dialog.ui</file><file>add_conversation/conference_details_fragment.ui</file><file>add_conversation/list_row.ui</file><file>add_conversation/select_jid_fragment.ui</file><file>call_widget.ui</file><file>chat_input.ui</file><file>contact_details_dialog.ui</file><file>conversation_list_titlebar.ui</file><file>conversation_list_titlebar_csd.ui</file><file>conversation_row.ui</file><file>conversation_view.ui</file><file>file_default_widget.ui</file><file>file_send_overlay.ui</file><file>emojichooser.ui</file><file>global_search.ui</file><file>conversation_content_view/item_metadata_header.ui</file><file>conversation_content_view/view.ui</file><file>manage_accounts/account_row.ui</file><file>manage_accounts/add_account_dialog.ui</file><file>manage_accounts/dialog.ui</file><file>menu_add.ui</file><file>menu_app.ui</file><file>menu_conversation.ui</file><file>menu_encryption.ui</file><file>message_item_widget_edit_mode.ui</file><file>occupant_list.ui</file><file>occupant_list_item.ui</file><file>search_autocomplete.ui</file><file>settings_dialog.ui</file><file>shortcuts.ui</file><file>unified_main_content.ui</file><file>unified_window_placeholder.ui</file><file>theme.css</file></gresource></gresources>
|
|
@ -348,7 +348,7 @@
|
|||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="max_content_height">300</property>
|
||||
<property name="max_content_height">128</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="visible">True</property>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="max_content_height">500</property>
|
||||
<property name="max_content_height">128</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
|
|
|
@ -148,6 +148,42 @@
|
|||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="send_button_checkbutton">
|
||||
<property name="label" translatable="yes">Display send button</property>
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="enter_newline_checkbutton">
|
||||
<property name="label" translatable="yes">Use Enter key to start a new line</property>
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="quit_ctrl_q_checkbutton">
|
||||
<property name="label" translatable="yes">Quit Dino with Ctrl+Q</property>
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
dependencies = [
|
||||
dep_dino,
|
||||
dep_gee,
|
||||
dep_glib,
|
||||
dep_gmodule,
|
||||
dep_gspell,
|
||||
dep_gtk3,
|
||||
dep_icu_uc,
|
||||
dep_libhandy,
|
||||
dep_m,
|
||||
dep_qlite,
|
||||
dep_xmpp_vala,
|
||||
]
|
||||
sources = files(
|
||||
'src/emojichooser.c',
|
||||
'src/main.vala',
|
||||
'src/ui/add_conversation/add_conference_dialog.vala',
|
||||
'src/ui/add_conversation/add_contact_dialog.vala',
|
||||
'src/ui/add_conversation/add_groupchat_dialog.vala',
|
||||
'src/ui/add_conversation/conference_details_fragment.vala',
|
||||
'src/ui/add_conversation/conference_list.vala',
|
||||
'src/ui/add_conversation/list_row.vala',
|
||||
'src/ui/add_conversation/roster_list.vala',
|
||||
'src/ui/add_conversation/select_contact_dialog.vala',
|
||||
'src/ui/add_conversation/select_jid_fragment.vala',
|
||||
'src/ui/avatar_drawer.vala',
|
||||
'src/ui/avatar_image.vala',
|
||||
'src/ui/application.vala',
|
||||
'src/ui/call_window/audio_settings_popover.vala',
|
||||
'src/ui/call_window/call_bottom_bar.vala',
|
||||
'src/ui/call_window/call_connection_details_window.vala',
|
||||
'src/ui/call_window/call_encryption_button.vala',
|
||||
'src/ui/call_window/call_window.vala',
|
||||
'src/ui/call_window/call_window_controller.vala',
|
||||
'src/ui/call_window/participant_widget.vala',
|
||||
'src/ui/call_window/video_settings_popover.vala',
|
||||
'src/ui/chat_input/chat_input_controller.vala',
|
||||
'src/ui/chat_input/chat_text_view.vala',
|
||||
'src/ui/chat_input/edit_history.vala',
|
||||
'src/ui/chat_input/encryption_button.vala',
|
||||
'src/ui/chat_input/occupants_tab_completer.vala',
|
||||
'src/ui/chat_input/smiley_converter.vala',
|
||||
'src/ui/chat_input/spell_checker.vala',
|
||||
'src/ui/chat_input/view.vala',
|
||||
'src/ui/contact_details/blocking_provider.vala',
|
||||
'src/ui/contact_details/dialog.vala',
|
||||
'src/ui/contact_details/muc_config_form_provider.vala',
|
||||
'src/ui/contact_details/permissions_provider.vala',
|
||||
'src/ui/contact_details/settings_provider.vala',
|
||||
'src/ui/conversation_content_view/call_widget.vala',
|
||||
'src/ui/conversation_content_view/chat_state_populator.vala',
|
||||
'src/ui/conversation_content_view/content_populator.vala',
|
||||
'src/ui/conversation_content_view/conversation_item_skeleton.vala',
|
||||
'src/ui/conversation_content_view/conversation_view.vala',
|
||||
'src/ui/conversation_content_view/date_separator_populator.vala',
|
||||
'src/ui/conversation_content_view/file_default_widget.vala',
|
||||
'src/ui/conversation_content_view/file_image_widget.vala',
|
||||
'src/ui/conversation_content_view/file_widget.vala',
|
||||
'src/ui/conversation_content_view/message_widget.vala',
|
||||
'src/ui/conversation_content_view/subscription_notification.vala',
|
||||
'src/ui/conversation_list_titlebar.vala',
|
||||
'src/ui/conversation_selector/conversation_selector.vala',
|
||||
'src/ui/conversation_selector/conversation_selector_row.vala',
|
||||
'src/ui/conversation_titlebar/call_entry.vala',
|
||||
'src/ui/conversation_titlebar/conversation_titlebar.vala',
|
||||
'src/ui/conversation_titlebar/menu_entry.vala',
|
||||
'src/ui/conversation_titlebar/occupants_entry.vala',
|
||||
'src/ui/conversation_titlebar/search_entry.vala',
|
||||
'src/ui/conversation_view.vala',
|
||||
'src/ui/conversation_view_controller.vala',
|
||||
'src/ui/file_send_overlay.vala',
|
||||
'src/ui/global_search.vala',
|
||||
'src/ui/main_window.vala',
|
||||
'src/ui/main_window_controller.vala',
|
||||
'src/ui/manage_accounts/account_row.vala',
|
||||
'src/ui/manage_accounts/add_account_dialog.vala',
|
||||
'src/ui/manage_accounts/dialog.vala',
|
||||
'src/ui/notifier_freedesktop.vala',
|
||||
'src/ui/notifier_gnotifications.vala',
|
||||
'src/ui/occupant_menu/list.vala',
|
||||
'src/ui/occupant_menu/list_row.vala',
|
||||
'src/ui/occupant_menu/view.vala',
|
||||
'src/ui/settings_dialog.vala',
|
||||
'src/ui/util/accounts_combo_box.vala',
|
||||
'src/ui/util/config.vala',
|
||||
'src/ui/util/data_forms.vala',
|
||||
'src/ui/util/helper.vala',
|
||||
'src/ui/util/label_hybrid.vala',
|
||||
'src/ui/util/preview_file_chooser_native.vala',
|
||||
'src/ui/util/scaling_image.vala',
|
||||
'src/ui/util/size_request_box.vala',
|
||||
'src/ui/util/sizing_bin.vala',
|
||||
'vapi/emojichooser.vapi',
|
||||
)
|
||||
|
||||
sources += import('gnome').compile_resources(
|
||||
'dino-resources',
|
||||
'data/gresource.xml',
|
||||
source_dir: 'data',
|
||||
)
|
||||
|
||||
c_args = [
|
||||
'-DG_LOG_DOMAIN="dino"',
|
||||
'-DGETTEXT_PACKAGE="dino"',
|
||||
'-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')),
|
||||
]
|
||||
exe_dino = executable('dino', sources, include_directories: 'src', c_args: c_args, vala_args: ['--vapidir', meson.current_source_dir() / 'vapi'], dependencies: dependencies)
|
|
@ -117,6 +117,8 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
|
|||
}
|
||||
|
||||
private void create_actions() {
|
||||
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
|
||||
|
||||
SimpleAction accounts_action = new SimpleAction("accounts", null);
|
||||
accounts_action.activate.connect(show_accounts_window);
|
||||
add_action(accounts_action);
|
||||
|
@ -132,7 +134,20 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
|
|||
SimpleAction quit_action = new SimpleAction("quit", null);
|
||||
quit_action.activate.connect(quit);
|
||||
add_action(quit_action);
|
||||
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
|
||||
|
||||
if (settings.quit_ctrl_q) {
|
||||
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
|
||||
}
|
||||
|
||||
settings.quit_ctrl_q_update.connect((active) =>
|
||||
{
|
||||
if (active) {
|
||||
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
|
||||
}
|
||||
else {
|
||||
set_accels_for_action("app.quit", {null});
|
||||
}
|
||||
});
|
||||
|
||||
SimpleAction open_conversation_action = new SimpleAction("open-conversation", VariantType.INT32);
|
||||
open_conversation_action.activate.connect((variant) => {
|
||||
|
@ -327,5 +342,20 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
|
|||
});
|
||||
dialog.present();
|
||||
}
|
||||
}
|
||||
|
||||
private uint inhibit_app(string reason) {
|
||||
uint inhibit_cookie = inhibit(window, SUSPEND, reason);
|
||||
|
||||
if (inhibit_cookie == 0) {
|
||||
warning("suspend inhibit request failed or unsupported");
|
||||
}
|
||||
|
||||
return inhibit_cookie;
|
||||
}
|
||||
|
||||
private void uninhibit_app(uint inhibit_cookie) {
|
||||
if (inhibit_cookie != 0) {
|
||||
uninhibit(inhibit_cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ChatTextView : ScrolledWindow {
|
|||
private SpellChecker spell_checker;
|
||||
|
||||
construct {
|
||||
max_content_height = 300;
|
||||
max_content_height = 128;
|
||||
propagate_natural_height = true;
|
||||
this.add(text_view);
|
||||
|
||||
|
@ -76,8 +76,11 @@ public class ChatTextView : ScrolledWindow {
|
|||
|
||||
private bool on_text_input_key_press(EventKey event) {
|
||||
if (event.keyval in new uint[]{Key.Return, Key.KP_Enter}) {
|
||||
if ((event.state & ModifierType.CONTROL_MASK) > 0) {
|
||||
text_view.buffer.insert_at_cursor("\n", 1);
|
||||
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
|
||||
|
||||
if ((event.state & ModifierType.SHIFT_MASK) > 0
|
||||
|| settings.enter_newline) {
|
||||
text_view.buffer.insert_at_cursor("\n", 1);
|
||||
} else if (text_view.buffer.text.strip() != "") {
|
||||
send_text();
|
||||
edit_history.reset_history();
|
||||
|
|
|
@ -55,39 +55,43 @@ public class View : Box {
|
|||
|
||||
outer_box.add(encryption_widget);
|
||||
|
||||
{
|
||||
MenuButton send_button = new MenuButton() {
|
||||
tooltip_text="Send message",
|
||||
relief=ReliefStyle.NONE,
|
||||
margin_top=3,
|
||||
valign=Align.CENTER,
|
||||
visible=true,
|
||||
sensitive=false
|
||||
};
|
||||
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
|
||||
|
||||
send_button.get_style_context().add_class("flat");
|
||||
send_button.get_style_context().add_class("dino-chatinput-button");
|
||||
send_button.image = new Image.from_icon_name("document-send", IconSize.BUTTON) {
|
||||
visible=true,
|
||||
icon_size=3
|
||||
};
|
||||
MenuButton send_button = new MenuButton() {
|
||||
tooltip_text="Send message",
|
||||
relief=ReliefStyle.NONE,
|
||||
margin_top=3,
|
||||
valign=Align.CENTER,
|
||||
visible=settings.send_button,
|
||||
sensitive=false
|
||||
};
|
||||
|
||||
chat_text_view.text_view.buffer.changed.connect(() => {
|
||||
if (chat_text_view.text_view.buffer.text != "") {
|
||||
send_button.sensitive = true;
|
||||
}
|
||||
else {
|
||||
send_button.sensitive = false;
|
||||
}
|
||||
});
|
||||
settings.send_button_update.connect(() => {
|
||||
send_button.visible = settings.send_button;
|
||||
});
|
||||
|
||||
send_button.button_release_event.connect(() => {
|
||||
chat_text_view.send_text();
|
||||
return true;
|
||||
});
|
||||
send_button.get_style_context().add_class("flat");
|
||||
send_button.get_style_context().add_class("dino-chatinput-button");
|
||||
send_button.image = new Image.from_icon_name("document-send", IconSize.BUTTON) {
|
||||
visible=true,
|
||||
icon_size=3
|
||||
};
|
||||
|
||||
outer_box.add(send_button);
|
||||
}
|
||||
chat_text_view.text_view.buffer.changed.connect(() => {
|
||||
if (chat_text_view.text_view.buffer.text != "") {
|
||||
send_button.sensitive = true;
|
||||
}
|
||||
else {
|
||||
send_button.sensitive = false;
|
||||
}
|
||||
});
|
||||
|
||||
send_button.button_release_event.connect(() => {
|
||||
chat_text_view.send_text();
|
||||
return true;
|
||||
});
|
||||
|
||||
outer_box.add(send_button);
|
||||
|
||||
Util.force_css(frame, "* { border-radius: 3px; }");
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Dino.Ui.ConversationSummary {
|
|||
public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins.NotificationCollection {
|
||||
|
||||
public signal void on_quote_text(string text);
|
||||
public signal void on_copy_text(string text);
|
||||
public Conversation? conversation { get; private set; }
|
||||
|
||||
[GtkChild] public unowned ScrolledWindow scrolled;
|
||||
|
@ -330,6 +331,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
|
|||
|
||||
MessageMetaItem current_item = item as MessageMetaItem;
|
||||
current_item.on_quote_text.connect((t, text) => on_quote_text(text));
|
||||
current_item.on_copy_text.connect((t, text) => on_copy_text(text));
|
||||
}
|
||||
meta_items.add(item);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ public class FileDefaultWidget : EventBox {
|
|||
cancel_button = new ModelButton() { text=_("Cancel"), visible=true };
|
||||
}
|
||||
|
||||
public void update_file_info(string? mime_type, FileTransfer.State state, long size) {
|
||||
public void update_file_info(string? mime_type, uint64 transferred_bytes,
|
||||
bool direction, FileTransfer.State state, long size) {
|
||||
this.state = state;
|
||||
|
||||
spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap
|
||||
|
@ -58,7 +59,17 @@ public class FileDefaultWidget : EventBox {
|
|||
popover_menu.closed.connect(on_pointer_left);
|
||||
break;
|
||||
case FileTransfer.State.IN_PROGRESS:
|
||||
mime_label.label = _("Downloading %s…").printf(get_size_string(size));
|
||||
uint progress = 0;
|
||||
|
||||
if (size > 0)
|
||||
progress = (uint)((transferred_bytes * (uint64)100) / (uint64)size);
|
||||
|
||||
if (direction == FileTransfer.DIRECTION_SENT) {
|
||||
mime_label.label = _("Uploading %s (%u%%)…").printf(get_size_string(size), progress);
|
||||
}
|
||||
else {
|
||||
mime_label.label = _("Downloading %s (%u%%)…").printf(get_size_string(size), progress);
|
||||
}
|
||||
spinner.active = true;
|
||||
image_stack.set_visible_child_name("spinner");
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ public class FileWidget : SizeRequestBox {
|
|||
}
|
||||
|
||||
private async void update_widget() {
|
||||
if (show_image() && state != State.IMAGE) {
|
||||
if (show_image() && state != State.IMAGE
|
||||
&& file_transfer.state == FileTransfer.State.COMPLETE) {
|
||||
var content_bak = content;
|
||||
|
||||
FileImageWidget file_image_widget = null;
|
||||
|
@ -120,6 +121,7 @@ public class FileDefaultWidgetController : Object {
|
|||
public string file_transfer_path { get; set; }
|
||||
public string file_transfer_state { get; set; }
|
||||
public string file_transfer_mime_type { get; set; }
|
||||
public uint64 file_transfer_transferred_bytes { get; set; }
|
||||
|
||||
private StreamInteractor? stream_interactor;
|
||||
private string file_uri;
|
||||
|
@ -143,10 +145,12 @@ public class FileDefaultWidgetController : Object {
|
|||
file_transfer.bind_property("path", this, "file-transfer-path");
|
||||
file_transfer.bind_property("state", this, "file-transfer-state");
|
||||
file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
|
||||
file_transfer.bind_property("transferred-bytes", this, "file-transfer-transferred-bytes");
|
||||
|
||||
this.notify["file-transfer-path"].connect(update_file_info);
|
||||
this.notify["file-transfer-state"].connect(update_file_info);
|
||||
this.notify["file-transfer-mime-type"].connect(update_file_info);
|
||||
this.notify["file-transfer-transferred-bytes"].connect(update_file_info);
|
||||
|
||||
update_file_info();
|
||||
}
|
||||
|
@ -155,13 +159,15 @@ public class FileDefaultWidgetController : Object {
|
|||
file_uri = file.get_uri();
|
||||
state = FileTransfer.State.COMPLETE;
|
||||
widget.name_label.label = this.file_name = file_name;
|
||||
widget.update_file_info(mime_type, state, -1);
|
||||
widget.update_file_info(mime_type, 0, false, state, -1);
|
||||
}
|
||||
|
||||
private void update_file_info() {
|
||||
file_uri = file_transfer.get_file().get_uri();
|
||||
state = file_transfer.state;
|
||||
widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
|
||||
widget.update_file_info(file_transfer.mime_type,
|
||||
file_transfer.transferred_bytes,
|
||||
file_transfer.direction, file_transfer.state, file_transfer.size);
|
||||
}
|
||||
|
||||
private void open_file() {
|
||||
|
@ -198,12 +204,13 @@ public class FileDefaultWidgetController : Object {
|
|||
open_file();
|
||||
}
|
||||
break;
|
||||
case FileTransfer.State.FAILED:
|
||||
case FileTransfer.State.NOT_STARTED:
|
||||
assert(stream_interactor != null && file_transfer != null);
|
||||
stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer);
|
||||
break;
|
||||
default:
|
||||
// Clicking doesn't do anything in FAILED and IN_PROGRESS states
|
||||
// Clicking doesn't do anything in IN_PROGRESS states
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Dino.Ui.ConversationSummary {
|
|||
public class MessageMetaItem : ContentMetaItem {
|
||||
|
||||
public signal void on_quote_text(string text);
|
||||
public signal void on_copy_text(string text);
|
||||
private StreamInteractor stream_interactor;
|
||||
private MessageItemWidget message_item_widget;
|
||||
private MessageItem message_item;
|
||||
|
@ -56,6 +57,14 @@ public class MessageMetaItem : ContentMetaItem {
|
|||
};
|
||||
actions.add(action2);
|
||||
|
||||
Plugins.MessageAction copy_action = new Plugins.MessageAction();
|
||||
copy_action.icon_name = "edit-copy";
|
||||
copy_action.callback = (button, content_meta_item_activated, widget) => {
|
||||
string text = ((MessageItem) message_item_widget.content_item).message.body;
|
||||
on_copy_text(text);
|
||||
};
|
||||
actions.add(copy_action);
|
||||
|
||||
if (allowed && !in_edit_mode) {
|
||||
Plugins.MessageAction action1 = new Plugins.MessageAction();
|
||||
action1.icon_name = "document-edit-symbolic";
|
||||
|
|
|
@ -14,10 +14,12 @@ public class ConversationView : Gtk.Overlay {
|
|||
[GtkChild] public unowned ChatInput.View chat_input;
|
||||
[GtkChild] public unowned ConversationSummary.ConversationView conversation_frame;
|
||||
[GtkChild] public unowned Revealer white_revealer;
|
||||
public signal void copy_text(string text);
|
||||
|
||||
construct {
|
||||
white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed);
|
||||
conversation_frame.on_quote_text.connect((t, text) => on_quote_text(text));
|
||||
conversation_frame.on_copy_text.connect((t, text) => copy_text(text));
|
||||
}
|
||||
|
||||
public void on_quote_text(string text) {
|
||||
|
|
|
@ -74,7 +74,8 @@ public class FileSendOverlay : Gtk.EventBox {
|
|||
if (widget == null) {
|
||||
FileDefaultWidget default_widget = new FileDefaultWidget() { visible=true };
|
||||
default_widget.name_label.label = file_name;
|
||||
default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size());
|
||||
default_widget.update_file_info(mime_type, 0, FileTransfer.DIRECTION_SENT,
|
||||
FileTransfer.State.COMPLETE, (long)file_info.get_size());
|
||||
widget = default_widget;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,12 @@ public class MainWindow : Gtk.Window {
|
|||
search_entry = (SearchEntry) builder.get_object("search_entry");
|
||||
Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image");
|
||||
conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/icons/dino-conversation-list-placeholder-arrow.svg"));
|
||||
|
||||
conversation_view.copy_text.connect((text) => {
|
||||
var display = get_display();
|
||||
var clipboard = Gtk.Clipboard.get_default(display);
|
||||
clipboard.set_text(text, text.length);
|
||||
});
|
||||
}
|
||||
|
||||
private void update_headerbar() {
|
||||
|
|
|
@ -14,6 +14,9 @@ class SettingsDialog : Dialog {
|
|||
[GtkChild] private unowned RadioButton encryption_radio_undecided;
|
||||
[GtkChild] private unowned RadioButton encryption_radio_omemo;
|
||||
[GtkChild] private unowned RadioButton encryption_radio_openpgp;
|
||||
[GtkChild] private unowned CheckButton send_button_checkbutton;
|
||||
[GtkChild] private unowned CheckButton enter_newline_checkbutton;
|
||||
[GtkChild] private unowned CheckButton quit_ctrl_q_checkbutton;
|
||||
|
||||
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
|
||||
|
||||
|
@ -28,6 +31,10 @@ class SettingsDialog : Dialog {
|
|||
encryption_radio_undecided.active = settings.default_encryption == Encryption.UNKNOWN;
|
||||
encryption_radio_omemo.active = settings.default_encryption == Encryption.OMEMO;
|
||||
encryption_radio_openpgp.active = settings.default_encryption == Encryption.PGP;
|
||||
send_button_checkbutton.active = settings.send_button;
|
||||
enter_newline_checkbutton.active = settings.enter_newline;
|
||||
enter_newline_checkbutton.sensitive = settings.send_button;
|
||||
quit_ctrl_q_checkbutton.active = settings.quit_ctrl_q;
|
||||
|
||||
typing_checkbutton.toggled.connect(() => { settings.send_typing = typing_checkbutton.active; } );
|
||||
marker_checkbutton.toggled.connect(() => { settings.send_marker = marker_checkbutton.active; } );
|
||||
|
@ -52,6 +59,17 @@ class SettingsDialog : Dialog {
|
|||
settings.default_encryption = Encryption.PGP;
|
||||
}
|
||||
});
|
||||
|
||||
send_button_checkbutton.toggled.connect(() => { settings.send_button = send_button_checkbutton.active; });
|
||||
enter_newline_checkbutton.toggled.connect(() => { settings.enter_newline = enter_newline_checkbutton.active; });
|
||||
settings.send_button_update.connect((visible) => {
|
||||
enter_newline_checkbutton.sensitive = visible;
|
||||
|
||||
if (visible == false) {
|
||||
enter_newline_checkbutton.active = visible;
|
||||
}
|
||||
});
|
||||
quit_ctrl_q_checkbutton.toggled.connect(() => { settings.quit_ctrl_q = quit_ctrl_q_checkbutton.active; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
project('dino', 'vala', 'c')
|
||||
|
||||
fs = import('fs')
|
||||
python = import('python')
|
||||
|
||||
dep_gcrypt = dependency('libgcrypt')
|
||||
dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0')
|
||||
dep_gee = dependency('gee-0.8')
|
||||
dep_gio = dependency('gio-2.0')
|
||||
dep_glib = dependency('glib-2.0')
|
||||
dep_gmodule = dependency('gmodule-2.0')
|
||||
dep_gspell = dependency('gspell-1')
|
||||
dep_gtk3 = dependency('gtk+-3.0')
|
||||
dep_icu_uc = dependency('icu-uc')
|
||||
dep_libhandy = dependency('libhandy-1')
|
||||
dep_m = meson.get_compiler('c').find_library('m', required: false)
|
||||
dep_sqlite3 = dependency('sqlite3', version: '>=3.24')
|
||||
dep_soup2 = dependency('libsoup-2.4')
|
||||
dep_soup3 = dependency('libsoup-3.0')
|
||||
dep_libsignal_protocol_c = dependency('libsignal-protocol-c', version: '>=2.3.2')
|
||||
|
||||
prog_git = find_program('git', required: false)
|
||||
prog_python = python.find_installation()
|
||||
|
||||
subdir('qlite')
|
||||
subdir('xmpp-vala')
|
||||
subdir('libdino')
|
||||
subdir('main')
|
||||
subdir('plugins')
|
|
@ -0,0 +1 @@
|
|||
option('plugindir', type: 'string', value: 'lib/dino/plugins', description: 'Plugin directory for Dino plugins')
|
|
@ -0,0 +1,22 @@
|
|||
dependencies = [
|
||||
dep_gee,
|
||||
dep_glib,
|
||||
dep_gtk3,
|
||||
dep_gmodule,
|
||||
dep_soup2,
|
||||
dep_dino,
|
||||
dep_soup2,
|
||||
]
|
||||
|
||||
sources = files(
|
||||
'src/file_provider.vala',
|
||||
'src/file_sender.vala',
|
||||
'src/plugin.vala',
|
||||
'src/register_plugin.vala',
|
||||
)
|
||||
|
||||
lib_http_files = library('http-files',
|
||||
sources,
|
||||
c_args: c_args,
|
||||
include_directories: include_directories('src'),
|
||||
dependencies: dependencies)
|
|
@ -117,7 +117,9 @@ public class FileProvider : Dino.FileProvider, Object {
|
|||
});
|
||||
file_meta.mime_type = content_type;
|
||||
if (content_length != null) {
|
||||
file_meta.size = int64.parse(content_length);
|
||||
if (int64.try_parse(content_length, out file_meta.size) == false) {
|
||||
throw new FileReceiveError.GET_METADATA_FAILED("failed to parse size");
|
||||
}
|
||||
}
|
||||
|
||||
return file_meta;
|
||||
|
@ -152,10 +154,23 @@ public class FileProvider : Dino.FileProvider, Object {
|
|||
}
|
||||
|
||||
public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError {
|
||||
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
|
||||
if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation");
|
||||
var? cm_identity = stream_interactor.get_module(ConversationManager.IDENTITY);
|
||||
if (cm_identity == null) {
|
||||
throw new FileReceiveError.GET_METADATA_FAILED("null ConversationManager");
|
||||
}
|
||||
|
||||
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
|
||||
Conversation? conversation = cm_identity.get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
|
||||
if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation");
|
||||
else if (file_transfer.info == null) {
|
||||
throw new FileReceiveError.GET_METADATA_FAILED("null file_transfer.info");
|
||||
}
|
||||
|
||||
int info;
|
||||
bool result = int.try_parse(file_transfer.info, out info);
|
||||
if (result == false) {
|
||||
throw new FileReceiveError.GET_METADATA_FAILED("failed to parse file_transfer.info");
|
||||
}
|
||||
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(info, conversation);
|
||||
if (message == null) throw new FileReceiveError.GET_METADATA_FAILED("No message");
|
||||
|
||||
var file_meta = new HttpFileMeta();
|
||||
|
@ -170,10 +185,20 @@ public class FileProvider : Dino.FileProvider, Object {
|
|||
}
|
||||
|
||||
public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) {
|
||||
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
|
||||
var? cm_identity = stream_interactor.get_module(ConversationManager.IDENTITY);
|
||||
if (cm_identity == null) return null;
|
||||
|
||||
Conversation? conversation = cm_identity.get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
|
||||
if (conversation == null) return null;
|
||||
|
||||
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
|
||||
var? ms_identity = stream_interactor.get_module(MessageStorage.IDENTITY);
|
||||
if (ms_identity == null || file_transfer.info == null) return null;
|
||||
|
||||
int info;
|
||||
bool result = int.try_parse(file_transfer.info, out info);
|
||||
if (result == false) return null;
|
||||
|
||||
Message? message = ms_identity.get_message_by_id(info, conversation);
|
||||
if (message == null) return null;
|
||||
|
||||
var receive_data = new HttpFileReceiveData();
|
||||
|
|
|
@ -25,7 +25,11 @@ public class HttpFileSender : FileSender, Object {
|
|||
if (stream == null) return null;
|
||||
|
||||
try {
|
||||
var slot_result = yield stream_interactor.module_manager.get_module(file_transfer.account, Xmpp.Xep.HttpFileUpload.Module.IDENTITY).request_slot(stream, file_transfer.server_file_name, file_meta.size, file_meta.mime_type);
|
||||
var slot_result = yield stream_interactor.module_manager
|
||||
.get_module(file_transfer.account,
|
||||
Xmpp.Xep.HttpFileUpload.Module.IDENTITY)
|
||||
.request_slot(stream, file_transfer.server_file_name,
|
||||
file_meta.size, file_meta.mime_type);
|
||||
send_data.url_down = slot_result.url_get;
|
||||
send_data.url_up = slot_result.url_put;
|
||||
send_data.headers = slot_result.headers;
|
||||
|
@ -81,6 +85,10 @@ public class HttpFileSender : FileSender, Object {
|
|||
body.complete();
|
||||
return;
|
||||
}
|
||||
else if (read < 0) {
|
||||
warning("transfer_more_bytes: stream.read failed");
|
||||
return;
|
||||
}
|
||||
bytes.length = (int)read;
|
||||
body.append_buffer(new Soup.Buffer.take(bytes));
|
||||
}
|
||||
|
@ -102,19 +110,38 @@ public class HttpFileSender : FileSender, Object {
|
|||
put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
|
||||
put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
|
||||
#endif
|
||||
|
||||
file_transfer.transferred_bytes = 0;
|
||||
put_message.wrote_body_data.connect((chunk) => {
|
||||
if (file_transfer.size != 0) {
|
||||
#if SOUP_3_0
|
||||
file_transfer.transferred_bytes += chunk;
|
||||
#else
|
||||
file_transfer.transferred_bytes += chunk.length;
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var entry in file_send_data.headers.entries) {
|
||||
put_message.request_headers.append(entry.key, entry.value);
|
||||
}
|
||||
|
||||
uint inhibit_cookie = 0;
|
||||
try {
|
||||
inhibit_cookie = Application.get_default()
|
||||
.inhibit_app("Ongoing file upload");
|
||||
#if SOUP_3_0
|
||||
yield session.send_async(put_message, GLib.Priority.LOW, file_transfer.cancellable);
|
||||
#else
|
||||
yield session.send_async(put_message, file_transfer.cancellable);
|
||||
#endif
|
||||
Application.get_default().uninhibit_app(inhibit_cookie);
|
||||
|
||||
if (put_message.status_code < 200 || put_message.status_code >= 300) {
|
||||
throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(put_message.status_code.to_string()));
|
||||
}
|
||||
} catch (Error e) {
|
||||
Application.get_default().uninhibit_app(inhibit_cookie);
|
||||
throw new FileSendError.UPLOAD_FAILED("HTTP upload error: %s".printf(e.message));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
subdir('signal-protocol')
|
||||
#subdir('gpgme-vala')
|
||||
subdir('http-files')
|
||||
#subdir('ice')
|
||||
#subdir('rtp')
|
|
@ -8,9 +8,35 @@ public class Plugin : RootInterface, Object {
|
|||
public void registered(Dino.Application app) {
|
||||
this.app = app;
|
||||
Canberra.Context.create(out sound_context);
|
||||
string sound_id = "",
|
||||
description = "Unknown notification";
|
||||
|
||||
app.stream_interactor.get_module(NotificationEvents.IDENTITY).notify_content_item.connect((item, conversation) => {
|
||||
sound_context.play(0, Canberra.PROP_EVENT_ID, "message-new-instant", Canberra.PROP_EVENT_DESCRIPTION, "New Dino message");
|
||||
var? module = app.stream_interactor.get_module(NotificationEvents.IDENTITY);
|
||||
|
||||
if (module == null)
|
||||
return;
|
||||
|
||||
module.notify_content_item.connect((item, conversation) => {
|
||||
switch (item.type_) {
|
||||
case MessageItem.TYPE:
|
||||
description = "New Dino message";
|
||||
sound_id = "message-new-instant";
|
||||
break;
|
||||
case CallItem.TYPE:
|
||||
description = "Incoming call";
|
||||
sound_id = "phone-incoming-call";
|
||||
break;
|
||||
case FileItem.TYPE:
|
||||
description = "Incoming file";
|
||||
sound_id = "message-new-instant";
|
||||
break;
|
||||
}
|
||||
|
||||
sound_context.play(0,
|
||||
Canberra.PROP_EVENT_ID,
|
||||
sound_id,
|
||||
Canberra.PROP_EVENT_DESCRIPTION,
|
||||
description);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,14 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidge
|
|||
add(widget);
|
||||
widget.visible = true;
|
||||
} else {
|
||||
var dialog = new Gtk.MessageDialog(null, MODAL, WARNING, OK, null);
|
||||
|
||||
dialog.text = "Could not create GTK video sink";
|
||||
dialog.secondary_text = "Please install package gstreamer1.0-gtk3 or equivalent.";
|
||||
dialog.response.connect((response_id) => {
|
||||
dialog.close();
|
||||
});
|
||||
dialog.run();
|
||||
warning("Could not create GTK video sink. Won't display videos.");
|
||||
}
|
||||
size_allocate.connect_after(after_size_allocate);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
dependencies = [
|
||||
dep_gee,
|
||||
dep_glib,
|
||||
dep_gcrypt,
|
||||
dep_dino,
|
||||
dep_libsignal_protocol_c,
|
||||
]
|
||||
|
||||
sources = files(
|
||||
'src/context.vala',
|
||||
'src/simple_iks.vala',
|
||||
'src/simple_ss.vala',
|
||||
'src/simple_pks.vala',
|
||||
'src/simple_spks.vala',
|
||||
'src/store.vala',
|
||||
'src/util.vala',
|
||||
'vapi/signal-protocol-public.vapi',
|
||||
'vapi/signal-protocol-native.vapi',
|
||||
)
|
||||
|
||||
lib_signal_protocol = library('signal-protocol-vala',
|
||||
sources,
|
||||
c_args: c_args,
|
||||
vala_args: ['--vapidir', meson.current_source_dir() / 'vapi'],
|
||||
include_directories: include_directories('src'),
|
||||
dependencies: dependencies)
|
|
@ -0,0 +1,22 @@
|
|||
dependencies = [
|
||||
dep_gee,
|
||||
dep_glib,
|
||||
dep_sqlite3,
|
||||
]
|
||||
sources = files(
|
||||
'src/column.vala',
|
||||
'src/database.vala',
|
||||
'src/delete_builder.vala',
|
||||
'src/insert_builder.vala',
|
||||
'src/query_builder.vala',
|
||||
'src/row.vala',
|
||||
'src/statement_builder.vala',
|
||||
'src/table.vala',
|
||||
'src/update_builder.vala',
|
||||
'src/upsert_builder.vala',
|
||||
)
|
||||
c_args = [
|
||||
'-DG_LOG_DOMAIN="qlite"',
|
||||
]
|
||||
lib_qlite = library('qlite', sources, c_args: c_args, vala_args: ['--vapidir', meson.current_source_dir() / 'vapi'], dependencies: dependencies)
|
||||
dep_qlite = declare_dependency(link_with: lib_qlite, include_directories: include_directories('.'))
|
|
@ -0,0 +1,124 @@
|
|||
dependencies = [
|
||||
dep_gdk_pixbuf,
|
||||
dep_gee,
|
||||
dep_gio,
|
||||
dep_glib,
|
||||
dep_icu_uc,
|
||||
dep_m,
|
||||
]
|
||||
sources = files(
|
||||
'src/core/direct_tls_xmpp_stream.vala',
|
||||
'src/core/io_xmpp_stream.vala',
|
||||
'src/core/module_flag.vala',
|
||||
'src/core/namespace_state.vala',
|
||||
'src/core/stanza_attribute.vala',
|
||||
'src/core/stanza_node.vala',
|
||||
'src/core/stanza_reader.vala',
|
||||
'src/core/stanza_writer.vala',
|
||||
'src/core/starttls_xmpp_stream.vala',
|
||||
'src/core/stream_connect.vala',
|
||||
'src/core/tls_xmpp_stream.vala',
|
||||
'src/core/xmpp_log.vala',
|
||||
'src/core/xmpp_stream.vala',
|
||||
'src/glib_fixes.vapi',
|
||||
'src/module/bind.vala',
|
||||
'src/module/bookmarks_provider.vala',
|
||||
'src/module/conference.vala',
|
||||
'src/module/iq/module.vala',
|
||||
'src/module/iq/stanza.vala',
|
||||
'src/module/jid.vala',
|
||||
'src/module/message/module.vala',
|
||||
'src/module/message/stanza.vala',
|
||||
'src/module/presence/flag.vala',
|
||||
'src/module/presence/module.vala',
|
||||
'src/module/presence/stanza.vala',
|
||||
'src/module/roster/flag.vala',
|
||||
'src/module/roster/item.vala',
|
||||
'src/module/roster/module.vala',
|
||||
'src/module/roster/versioning_module.vala',
|
||||
'src/module/sasl.vala',
|
||||
'src/module/session.vala',
|
||||
'src/module/stanza.vala',
|
||||
'src/module/stanza_error.vala',
|
||||
'src/module/stream_error.vala',
|
||||
'src/module/util.vala',
|
||||
'src/module/xep/0004_data_forms.vala',
|
||||
'src/module/xep/0030_service_discovery/flag.vala',
|
||||
'src/module/xep/0030_service_discovery/identity.vala',
|
||||
'src/module/xep/0030_service_discovery/info_result.vala',
|
||||
'src/module/xep/0030_service_discovery/item.vala',
|
||||
'src/module/xep/0030_service_discovery/items_result.vala',
|
||||
'src/module/xep/0030_service_discovery/module.vala',
|
||||
'src/module/xep/0045_muc/flag.vala',
|
||||
'src/module/xep/0045_muc/module.vala',
|
||||
'src/module/xep/0045_muc/status_code.vala',
|
||||
'src/module/xep/0047_in_band_bytestreams.vala',
|
||||
'src/module/xep/0048_bookmarks.vala',
|
||||
'src/module/xep/0048_conference.vala',
|
||||
'src/module/xep/0049_private_xml_storage.vala',
|
||||
'src/module/xep/0054_vcard/module.vala',
|
||||
'src/module/xep/0060_pubsub.vala',
|
||||
'src/module/xep/0065_socks5_bytestreams.vala',
|
||||
'src/module/xep/0066_out_of_band_data.vala',
|
||||
'src/module/xep/0077_in_band_registration.vala',
|
||||
'src/module/xep/0082_date_time_profiles.vala',
|
||||
'src/module/xep/0084_user_avatars.vala',
|
||||
'src/module/xep/0085_chat_state_notifications.vala',
|
||||
'src/module/xep/0115_entity_capabilities.vala',
|
||||
'src/module/xep/0166_jingle/component.vala',
|
||||
'src/module/xep/0166_jingle/content.vala',
|
||||
'src/module/xep/0166_jingle/content_description.vala',
|
||||
'src/module/xep/0166_jingle/content_node.vala',
|
||||
'src/module/xep/0166_jingle/content_security.vala',
|
||||
'src/module/xep/0166_jingle/content_transport.vala',
|
||||
'src/module/xep/0166_jingle/jingle_flag.vala',
|
||||
'src/module/xep/0166_jingle/jingle_module.vala',
|
||||
'src/module/xep/0166_jingle/jingle_structs.vala',
|
||||
'src/module/xep/0166_jingle/reason_element.vala',
|
||||
'src/module/xep/0166_jingle/session.vala',
|
||||
'src/module/xep/0166_jingle/session_info.vala',
|
||||
'src/module/xep/0167_jingle_rtp/content_parameters.vala',
|
||||
'src/module/xep/0167_jingle_rtp/content_type.vala',
|
||||
'src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala',
|
||||
'src/module/xep/0167_jingle_rtp/payload_type.vala',
|
||||
'src/module/xep/0167_jingle_rtp/session_info_type.vala',
|
||||
'src/module/xep/0167_jingle_rtp/stream.vala',
|
||||
'src/module/xep/0176_jingle_ice_udp/candidate.vala',
|
||||
'src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala',
|
||||
'src/module/xep/0176_jingle_ice_udp/transport_parameters.vala',
|
||||
'src/module/xep/0177_jingle_raw_udp.vala',
|
||||
'src/module/xep/0184_message_delivery_receipts.vala',
|
||||
'src/module/xep/0191_blocking_command.vala',
|
||||
'src/module/xep/0198_stream_management.vala',
|
||||
'src/module/xep/0199_ping.vala',
|
||||
'src/module/xep/0203_delayed_delivery.vala',
|
||||
'src/module/xep/0215_external_service_discovery.vala',
|
||||
'src/module/xep/0234_jingle_file_transfer.vala',
|
||||
'src/module/xep/0249_direct_muc_invitations.vala',
|
||||
'src/module/xep/0260_jingle_socks5_bytestreams.vala',
|
||||
'src/module/xep/0261_jingle_in_band_bytestreams.vala',
|
||||
'src/module/xep/0272_muji.vala',
|
||||
'src/module/xep/0280_message_carbons.vala',
|
||||
'src/module/xep/0298_coin.vala',
|
||||
'src/module/xep/0308_last_message_correction.vala',
|
||||
'src/module/xep/0313_message_archive_management.vala',
|
||||
'src/module/xep/0333_chat_markers.vala',
|
||||
'src/module/xep/0334_message_processing_hints.vala',
|
||||
'src/module/xep/0353_call_invite_message.vala',
|
||||
'src/module/xep/0353_jingle_message_initiation.vala',
|
||||
'src/module/xep/0359_unique_stable_stanza_ids.vala',
|
||||
'src/module/xep/0363_http_file_upload.vala',
|
||||
'src/module/xep/0380_explicit_encryption.vala',
|
||||
'src/module/xep/0384_omemo/omemo_decryptor.vala',
|
||||
'src/module/xep/0384_omemo/omemo_encryptor.vala',
|
||||
'src/module/xep/0391_jingle_encrypted_transports.vala',
|
||||
'src/module/xep/0402_bookmarks2.vala',
|
||||
'src/module/xep/0410_muc_self_ping.vala',
|
||||
'src/module/xep/pixbuf_storage.vala',
|
||||
'src/util.vala',
|
||||
)
|
||||
c_args = [
|
||||
'-DG_LOG_DOMAIN="xmpp-vala"',
|
||||
]
|
||||
lib_xmpp_vala = library('xmpp-vala', sources, c_args: c_args, vala_args: ['--vapidir', meson.current_source_dir() / 'vapi'], dependencies: dependencies)
|
||||
dep_xmpp_vala = declare_dependency(link_with: lib_xmpp_vala, include_directories: include_directories('.'))
|
|
@ -8,6 +8,7 @@ private const string NS_URI = "urn:xmpp:jingle:apps:file-transfer:5";
|
|||
|
||||
public class Module : Jingle.ContentType, XmppStreamModule {
|
||||
|
||||
public signal void transferred_bytes(size_t bytes);
|
||||
public signal void file_incoming(XmppStream stream, FileTransfer file_transfer);
|
||||
|
||||
public static Xmpp.ModuleIdentity<Module> IDENTITY = new Xmpp.ModuleIdentity<Module>(NS_URI, "0234_jingle_file_transfer");
|
||||
|
@ -42,7 +43,10 @@ public class Module : Jingle.ContentType, XmppStreamModule {
|
|||
return yield stream.get_module(Jingle.Module.IDENTITY).is_available(stream, required_transport_type, required_components, full_jid);
|
||||
}
|
||||
|
||||
public async void offer_file_stream(XmppStream stream, Jid receiver_full_jid, InputStream input_stream, string basename, int64 size, string? precondition_name = null, Object? precondition_options = null) throws Jingle.Error {
|
||||
public async void offer_file_stream(XmppStream stream, Jid receiver_full_jid,
|
||||
Cancellable cancellable, InputStream input_stream, string basename,
|
||||
int64 size, string? precondition_name = null,
|
||||
Object? precondition_options = null) throws Jingle.Error {
|
||||
StanzaNode file_node;
|
||||
StanzaNode description = new StanzaNode.build("description", NS_URI)
|
||||
.add_self_xmlns()
|
||||
|
@ -107,7 +111,18 @@ public class Module : Jingle.ContentType, XmppStreamModule {
|
|||
}
|
||||
IOStream io_stream = yield connection.stream.wait_async();
|
||||
yield io_stream.input_stream.close_async();
|
||||
yield io_stream.output_stream.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
|
||||
|
||||
ssize_t read;
|
||||
var buffer = new uint8[1024];
|
||||
while ((read = yield input_stream.read_async(buffer, Priority.LOW, cancellable)) > 0) {
|
||||
buffer.length = (int) read;
|
||||
transferred_bytes((size_t)read);
|
||||
yield io_stream.output_stream.write_async(buffer, Priority.LOW, cancellable);
|
||||
buffer.length = 1024;
|
||||
}
|
||||
|
||||
yield input_stream.close_async();
|
||||
yield io_stream.output_stream.close_async();
|
||||
yield connection.terminate(true);
|
||||
} catch (Error e) {
|
||||
if (session != null) {
|
||||
|
|
|
@ -39,7 +39,11 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
|||
}
|
||||
}
|
||||
|
||||
private StanzaNode create_base_query(XmppStream stream, MamQueryParams mam_params) {
|
||||
private StanzaNode create_base_query(XmppStream? stream, MamQueryParams mam_params) {
|
||||
if (stream == null) {
|
||||
return new StanzaNode();
|
||||
}
|
||||
|
||||
var fields = new ArrayList<DataForms.DataForm.Field>();
|
||||
|
||||
if (mam_params.with != null) {
|
||||
|
@ -61,7 +65,12 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
|||
return MessageArchiveManagement.create_base_query(stream, MessageArchiveManagement.NS_URI_2, mam_params.query_id, fields);
|
||||
}
|
||||
|
||||
public async QueryResult query_archive(XmppStream stream, MamQueryParams mam_params, Cancellable? cancellable = null) {
|
||||
public async QueryResult query_archive(XmppStream? stream, MamQueryParams mam_params, Cancellable? cancellable = null) {
|
||||
if (stream == null) {
|
||||
var result = new QueryResult();
|
||||
result.error = true;
|
||||
return result;
|
||||
}
|
||||
var query_node = create_base_query(stream, mam_params);
|
||||
if (!mam_params.use_ns2_extended) {
|
||||
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id));
|
||||
|
|
|
@ -53,7 +53,11 @@ public class Module : XmppStreamModule {
|
|||
}
|
||||
}
|
||||
|
||||
internal StanzaNode create_base_query(XmppStream stream, string ns, string? queryid, Gee.List<DataForms.DataForm.Field> fields) {
|
||||
internal StanzaNode create_base_query(XmppStream? stream, string ns, string? queryid, Gee.List<DataForms.DataForm.Field> fields) {
|
||||
if (stream == null) {
|
||||
return new StanzaNode();
|
||||
}
|
||||
|
||||
DataForms.DataForm data_form = new DataForms.DataForm();
|
||||
|
||||
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
||||
|
@ -156,7 +160,11 @@ public class MessageFlag : Xmpp.MessageFlag {
|
|||
}
|
||||
|
||||
private static string NS_VER(XmppStream stream) {
|
||||
return stream.get_flag(Flag.IDENTITY).ns_ver;
|
||||
var? flag = stream.get_flag(Flag.IDENTITY);
|
||||
if (flag == null) {
|
||||
return "";
|
||||
}
|
||||
return flag.ns_ver;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue