Compare commits

...

16 Commits

Author SHA1 Message Date
Xavier Del Campo Romero 3359e83d52
Remove USE_SOUP3
CMake had already replaced it with SOUP_VERSION, so it made no sense for
configure to insist on USE_SOUP3.
2023-10-05 13:46:22 +02:00
Xavier Del Campo Romero 3676d4d074
SoupVersion.cmake: Force version required by Nice
Otherwise, Dino would crash due to conflicting Soup 2 and 3 symbols.
2023-10-05 13:46:22 +02:00
Xavier Del Campo Romero 77370c826d
file_widget.vala: Only show image when sent 2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero 5f6a146adc
connection_manager.vala: Use yield on disconnect() 2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero d02e8d36e9
file_sender.vala: Split very long line 2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero 6623262f3f
Check XmppStream against null
Most of the calls to stream_interactor.get_stream(account) were already
doing null checks, but there were still some missing.
2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero 19da202ecf
file_manager.vala: Disable automatic download 2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero e48b5636c5
Show file upload/download progress
Fixes upstream issue #1350.

Notes:

Image uploads were incorrectly handled by Dino, as they were always
reported as completed even if they were not, maybe so as to show the
image preview from the start. Now, Dino shows the upload progress for
all file types, and the image is only shown when completed.
2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero 7a956f5874
file_manager.vala: Unify downloads for soup2/3
Upstream commit 809c1579e4 avoided the use
of OutputStream.splice_async for soup3 in favour of one or more calls to
InputStream.read_async in a loop. However, the latter approach can still
be used successfully for soup2, so it makes sense to keep one
implementation for both versions.

This will also allow future commits to retrieve download progress.
2023-10-05 13:46:21 +02:00
Xavier Del Campo Romero e5fcd64618
http-files: Avoid null dereferences 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero 7689392c23
connection_manager.vala: Refactor reconnect stream 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero 67132f6491
history_sync.vala: Remove noisy debug 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero b76a3e7a49
Add screenshots 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero d68c9f4ac2
README.md: Update features 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero 689fc2846c
Allow to copy messages 2023-10-05 13:46:20 +02:00
Xavier Del Campo Romero 5d1a116aba
Add BACKPORT 2023-10-05 13:46:20 +02:00
24 changed files with 561 additions and 66 deletions

345
BACKPORT Normal file
View File

@ -0,0 +1,345 @@
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.
According to the commit message, the changes introduced by this commit
were meant for libsoup3 only. However, the commit did not wrap the
libsoup3-specific in #if SOUP_3, so this has been added for this
fork.
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.

View File

@ -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

View File

@ -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)

6
configure vendored
View File

@ -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" \

BIN
doc/screenshot-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
doc/screenshot-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -3,7 +3,7 @@ using Xmpp;
namespace Dino.Entities {
public class FileTransfer : Object {
public signal void progress_updated(int progress);
public const bool DIRECTION_SENT = true;
public const bool DIRECTION_RECEIVED = false;
@ -70,6 +70,14 @@ public class FileTransfer : Object {
public State state { get; set; default=State.NOT_STARTED; }
public int provider { get; set; }
public string info { get; set; }
public int progress_ { get; set; }
public int progress {
get {return progress_;}
set {
progress_ = value;
progress_updated(progress_);
}
}
public Cancellable cancellable { get; default=new Cancellable(); }
private Database? db;

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -59,12 +59,12 @@ public class ConnectionManager : Object {
reset();
}
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) {

View File

@ -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);
@ -249,19 +250,22 @@ public class FileManager : StreamInteractionModule, Object {
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
file_transfer.cancellable.reset();
#if !SOUP_3_0
yield os.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET, Priority.LOW, file_transfer.cancellable);
#else
uint8[] buffer = new uint8[1024];
ssize_t read;
uint64 received_bytes = 0;
while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) {
buffer.length = (int) read;
received_bytes += (size_t)read;
file_transfer.progress =
(int)((received_bytes * (uint64)100)
/ (uint64)file_transfer.size);
yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable);
buffer.length = 1024;
}
yield input_stream.close_async(Priority.LOW, file_transfer.cancellable);
yield os.close_async(Priority.LOW, file_transfer.cancellable);
#endif
file_transfer.path = file.get_basename();
file_transfer.input_stream = yield file.read_async();
@ -292,6 +296,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.progress = 0;
var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta);
if (encryption != Encryption.NONE) file_transfer.encryption = encryption;
@ -311,11 +316,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;
@ -328,7 +328,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) {

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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, int progress,
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,12 @@ 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));
if (direction == FileTransfer.DIRECTION_SENT) {
mime_label.label = _("Uploading %s (%d%%)…").printf(get_size_string(size), progress);
}
else {
mime_label.label = _("Downloading %s (%d%%)…").printf(get_size_string(size), progress);
}
spinner.active = true;
image_stack.set_visible_child_name("spinner");

View File

@ -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;
@ -148,6 +149,7 @@ public class FileDefaultWidgetController : Object {
this.notify["file-transfer-state"].connect(update_file_info);
this.notify["file-transfer-mime-type"].connect(update_file_info);
file_transfer.progress_updated.connect(update_file_info);
update_file_info();
}
@ -155,13 +157,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.progress,
file_transfer.direction, file_transfer.state, file_transfer.size);
}
private void open_file() {

View File

@ -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";

View File

@ -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) {

View File

@ -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, 100, FileTransfer.DIRECTION_SENT,
FileTransfer.State.COMPLETE, (long)file_info.get_size());
widget = default_widget;
}

View File

@ -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() {

View File

@ -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();

View File

@ -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,6 +110,22 @@ 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
uint64 sent_bytes = 0;
put_message.wrote_body_data.connect((chunk) => {
if (file_transfer.size != 0) {
#if SOUP_3_0
sent_bytes += chunk;
#else
sent_bytes += chunk.length;
#endif
file_transfer.progress =
(int)((sent_bytes * (uint64)100)
/ (uint64)file_transfer.size);
}
});
foreach (var entry in file_send_data.headers.entries) {
put_message.request_headers.append(entry.key, entry.value);
}

View File

@ -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));

View File

@ -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;
}
}