Merge remote-tracking branch 'upstream/master' into master-windows-changes
This commit is contained in:
commit
7603990740
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: sudo apt-get update
|
- run: sudo apt-get update
|
||||||
- run: sudo apt-get remove libunwind-14-dev
|
- run: sudo apt-get remove libunwind-14-dev
|
||||||
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev
|
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev libsignal-protocol-c-dev
|
||||||
- run: ./configure --with-tests --with-libsignal-in-tree
|
- run: ./configure --with-tests --with-libsignal-in-tree
|
||||||
- run: make
|
- run: make
|
||||||
- run: build/xmpp-vala-test
|
- run: build/xmpp-vala-test
|
||||||
|
@ -36,4 +36,5 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
||||||
with:
|
with:
|
||||||
manifest-path: im.dino.Dino.json
|
manifest-path: im.dino.Dino.json
|
||||||
|
build-bundle: false
|
||||||
|
|
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,4 +0,0 @@
|
||||||
[submodule "libsignal-protocol-c"]
|
|
||||||
path = plugins/signal-protocol/libsignal-protocol-c
|
|
||||||
url = https://github.com/WhisperSystems/libsignal-protocol-c.git
|
|
||||||
branch = v2.3.3
|
|
53
configure
vendored
53
configure
vendored
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
OPTS=`getopt -o "h" --long \
|
OPTS=`getopt -o "h" --long \
|
||||||
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,with-libsoup3,\
|
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsoup3,\
|
||||||
enable-plugin:,disable-plugin:,\
|
enable-plugin:,disable-plugin:,\
|
||||||
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
|
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
|
||||||
bindir:,libdir:,includedir:,datadir:,\
|
bindir:,libdir:,includedir:,datadir:,\
|
||||||
|
@ -15,13 +15,11 @@ eval set -- "$OPTS"
|
||||||
PREFIX=${PREFIX:-/usr/local}
|
PREFIX=${PREFIX:-/usr/local}
|
||||||
ENABLED_PLUGINS=
|
ENABLED_PLUGINS=
|
||||||
DISABLED_PLUGINS=
|
DISABLED_PLUGINS=
|
||||||
BUILD_LIBSIGNAL_IN_TREE=
|
|
||||||
BUILD_TESTS=
|
BUILD_TESTS=
|
||||||
BUILD_TYPE=Debug
|
BUILD_TYPE=Debug
|
||||||
DISABLE_FAST_VAPI=
|
DISABLE_FAST_VAPI=
|
||||||
LIB_SUFFIX=
|
LIB_SUFFIX=
|
||||||
NO_DEBUG=
|
NO_DEBUG=
|
||||||
FETCH_ONLY=
|
|
||||||
USE_SOUP3=
|
USE_SOUP3=
|
||||||
|
|
||||||
EXEC_PREFIX=
|
EXEC_PREFIX=
|
||||||
|
@ -55,8 +53,6 @@ Configuration:
|
||||||
without network access later and exit.
|
without network access later and exit.
|
||||||
--no-debug Build without debug symbols
|
--no-debug Build without debug symbols
|
||||||
--release Configure to build an optimized release version
|
--release Configure to build an optimized release version
|
||||||
--with-libsignal-in-tree Build libsignal-protocol-c in tree and link it
|
|
||||||
statically.
|
|
||||||
--with-libsoup3 Build with libsoup-3.0
|
--with-libsoup3 Build with libsoup-3.0
|
||||||
--with-tests Also build tests.
|
--with-tests Also build tests.
|
||||||
|
|
||||||
|
@ -112,11 +108,9 @@ while true; do
|
||||||
--valac ) VALA_EXECUTABLE="$2"; shift; shift ;;
|
--valac ) VALA_EXECUTABLE="$2"; shift; shift ;;
|
||||||
--valac-flags ) VALAC_FLAGS="$2"; shift; shift ;;
|
--valac-flags ) VALAC_FLAGS="$2"; shift; shift ;;
|
||||||
--lib-suffix ) LIB_SUFFIX="$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 ) USE_SOUP3=yes; shift ;;
|
||||||
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
|
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
|
||||||
--no-debug ) NO_DEBUG=yes; shift ;;
|
--no-debug ) NO_DEBUG=yes; shift ;;
|
||||||
--fetch-only ) FETCH_ONLY=yes; shift ;;
|
|
||||||
--release ) BUILD_TYPE=RelWithDebInfo; shift ;;
|
--release ) BUILD_TYPE=RelWithDebInfo; shift ;;
|
||||||
--with-tests ) BUILD_TESTS=yes; shift ;;
|
--with-tests ) BUILD_TESTS=yes; shift ;;
|
||||||
# Autotools paths
|
# Autotools paths
|
||||||
|
@ -141,50 +135,6 @@ while true; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$BUILD_LIBSIGNAL_IN_TREE" = "yes" ] || [ "$FETCH_ONLY" = "yes" ]; then
|
|
||||||
if [ -d ".git" ]; then
|
|
||||||
git submodule update --init 2>/dev/null
|
|
||||||
else
|
|
||||||
tmp=0
|
|
||||||
for i in $(cat .gitmodules | grep -n submodule | awk -F ':' '{print $1}') $(wc -l .gitmodules | awk '{print $1}'); do
|
|
||||||
if ! [ $tmp -eq 0 ]; then
|
|
||||||
name=$(cat .gitmodules | head -n $tmp | tail -n 1 | awk -F '"' '{print $2}')
|
|
||||||
def=$(cat .gitmodules | head -n $i | tail -n $(expr "$i" - "$tmp") | awk -F ' ' '{print $1 $2 $3}')
|
|
||||||
path=$(echo "$def" | grep '^path=' | awk -F '=' '{print $2}')
|
|
||||||
url=$(echo "$def" | grep '^url=' | awk -F '=' '{print $2}')
|
|
||||||
branch=$(echo "$def" | grep '^branch=' | awk -F '=' '{print $2}')
|
|
||||||
|
|
||||||
if ! ls "$path"/* >/dev/null 2>/dev/null; then
|
|
||||||
git=$(which git)
|
|
||||||
if ! [ $? -eq 0 ] || ! [ -x $git ]; then
|
|
||||||
echo "Failed retrieving missing files"
|
|
||||||
exit 5
|
|
||||||
fi
|
|
||||||
res=$(git clone "$url" "$path" 2>&1)
|
|
||||||
if ! [ $? -eq 0 ] || ! [ -d $path ]; then
|
|
||||||
echo "Failed retrieving missing files: $res"
|
|
||||||
exit 5
|
|
||||||
fi
|
|
||||||
if [ -n "$branch" ]; then
|
|
||||||
olddir="$(pwd)"
|
|
||||||
cd "$path"
|
|
||||||
res=$(git checkout "$branch" 2>&1)
|
|
||||||
if ! [ $? -eq 0 ]; then
|
|
||||||
echo "Failed retrieving missing files: $res"
|
|
||||||
exit 5
|
|
||||||
fi
|
|
||||||
cd "$olddir"
|
|
||||||
fi
|
|
||||||
echo "Submodule path '$path': checked out '$branch' (via git clone)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
tmp=$i
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$FETCH_ONLY" = "yes" ]; then exit 0; fi
|
|
||||||
|
|
||||||
if [ ! -x "$(which cmake 2>/dev/null)" ]
|
if [ ! -x "$(which cmake 2>/dev/null)" ]
|
||||||
then
|
then
|
||||||
echo "-!- CMake required."
|
echo "-!- CMake required."
|
||||||
|
@ -258,7 +208,6 @@ cmake -G "$cmake_type" \
|
||||||
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \
|
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \
|
||||||
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
|
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
|
||||||
-DBUILD_TESTS="$BUILD_TESTS" \
|
-DBUILD_TESTS="$BUILD_TESTS" \
|
||||||
-DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \
|
|
||||||
-DUSE_SOUP3="$USE_SOUP3" \
|
-DUSE_SOUP3="$USE_SOUP3" \
|
||||||
-DVALA_EXECUTABLE="$VALAC" \
|
-DVALA_EXECUTABLE="$VALAC" \
|
||||||
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
|
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
"--socket=wayland",
|
"--socket=wayland",
|
||||||
"--socket=pulseaudio",
|
"--socket=pulseaudio",
|
||||||
"--socket=gpg-agent",
|
"--socket=gpg-agent",
|
||||||
|
"--filesystem=xdg-run/pipewire-0",
|
||||||
"--share=network",
|
"--share=network",
|
||||||
"--device=all",
|
"--device=dri",
|
||||||
"--talk-name=org.freedesktop.Notifications"
|
"--talk-name=org.freedesktop.Notifications"
|
||||||
],
|
],
|
||||||
"modules": [
|
"modules": [
|
||||||
|
|
|
@ -18,7 +18,6 @@ public class Account : Object {
|
||||||
public string? alias { get; set; }
|
public string? alias { get; set; }
|
||||||
public bool enabled { get; set; default = false; }
|
public bool enabled { get; set; default = false; }
|
||||||
public string? roster_version { get; set; }
|
public string? roster_version { get; set; }
|
||||||
public DateTime mam_earliest_synced { get; set; default=new DateTime.from_unix_utc(0); }
|
|
||||||
|
|
||||||
private Database? db;
|
private Database? db;
|
||||||
|
|
||||||
|
@ -50,7 +49,6 @@ public class Account : Object {
|
||||||
alias = row[db.account.alias];
|
alias = row[db.account.alias];
|
||||||
enabled = row[db.account.enabled];
|
enabled = row[db.account.enabled];
|
||||||
roster_version = row[db.account.roster_version];
|
roster_version = row[db.account.roster_version];
|
||||||
mam_earliest_synced = new DateTime.from_unix_utc(row[db.account.mam_earliest_synced]);
|
|
||||||
|
|
||||||
notify.connect(on_update);
|
notify.connect(on_update);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +64,6 @@ public class Account : Object {
|
||||||
.value(db.account.alias, alias)
|
.value(db.account.alias, alias)
|
||||||
.value(db.account.enabled, enabled)
|
.value(db.account.enabled, enabled)
|
||||||
.value(db.account.roster_version, roster_version)
|
.value(db.account.roster_version, roster_version)
|
||||||
.value(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix())
|
|
||||||
.perform();
|
.perform();
|
||||||
|
|
||||||
notify.connect(on_update);
|
notify.connect(on_update);
|
||||||
|
@ -106,8 +103,6 @@ public class Account : Object {
|
||||||
update.set(db.account.enabled, enabled); break;
|
update.set(db.account.enabled, enabled); break;
|
||||||
case "roster-version":
|
case "roster-version":
|
||||||
update.set(db.account.roster_version, roster_version); break;
|
update.set(db.account.roster_version, roster_version); break;
|
||||||
case "mam-earliest-synced":
|
|
||||||
update.set(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()); break;
|
|
||||||
}
|
}
|
||||||
update.perform();
|
update.perform();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class Database : Qlite.Database {
|
||||||
public Column<string> alias = new Column.Text("alias");
|
public Column<string> alias = new Column.Text("alias");
|
||||||
public Column<bool> enabled = new Column.BoolInt("enabled");
|
public Column<bool> enabled = new Column.BoolInt("enabled");
|
||||||
public Column<string> roster_version = new Column.Text("roster_version") { min_version=2 };
|
public Column<string> roster_version = new Column.Text("roster_version") { min_version=2 };
|
||||||
|
// no longer used. all usages already removed. remove db column at some point.
|
||||||
public Column<long> mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 };
|
public Column<long> mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 };
|
||||||
|
|
||||||
internal AccountTable(Database db) {
|
internal AccountTable(Database db) {
|
||||||
|
|
|
@ -90,11 +90,9 @@ public class Dino.HistorySync {
|
||||||
if (!is_muc_mam && !from_our_server) return;
|
if (!is_muc_mam && !from_our_server) return;
|
||||||
|
|
||||||
// Get the server time of the message and store it in `mam_times`
|
// Get the server time of the message and store it in `mam_times`
|
||||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
string? id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", "id");
|
||||||
if (mam_flag == null) return;
|
|
||||||
string? id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", "id");
|
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
StanzaNode? delay_node = message.stanza.get_deep_subnode(mam_flag.ns_ver + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
StanzaNode? delay_node = message.stanza.get_deep_subnode(Xmpp.MessageArchiveManagement.NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||||
if (delay_node == null) {
|
if (delay_node == null) {
|
||||||
warning("MAM result did not contain delayed time %s", message.stanza.to_string());
|
warning("MAM result did not contain delayed time %s", message.stanza.to_string());
|
||||||
return;
|
return;
|
||||||
|
@ -104,7 +102,7 @@ public class Dino.HistorySync {
|
||||||
mam_times[account][id] = time;
|
mam_times[account][id] = time;
|
||||||
|
|
||||||
// Check if this is the target message
|
// Check if this is the target message
|
||||||
string? query_id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", mam_flag.ns_ver + ":queryid");
|
string? query_id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", Xmpp.MessageArchiveManagement.NS_URI + ":queryid");
|
||||||
if (query_id != null && id == catchup_until_id[account]) {
|
if (query_id != null && id == catchup_until_id[account]) {
|
||||||
debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id);
|
debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id);
|
||||||
hitted_range[query_id] = -2;
|
hitted_range[query_id] = -2;
|
||||||
|
|
|
@ -38,7 +38,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
||||||
received_pipeline.connect(new FilterMessageListener());
|
received_pipeline.connect(new FilterMessageListener());
|
||||||
received_pipeline.connect(new StoreMessageListener(this, stream_interactor));
|
received_pipeline.connect(new StoreMessageListener(this, stream_interactor));
|
||||||
received_pipeline.connect(new StoreContentItemListener(stream_interactor));
|
received_pipeline.connect(new StoreContentItemListener(stream_interactor));
|
||||||
received_pipeline.connect(new MamMessageListener(stream_interactor));
|
|
||||||
|
|
||||||
stream_interactor.account_added.connect(on_account_added);
|
stream_interactor.account_added.connect(on_account_added);
|
||||||
|
|
||||||
|
@ -170,19 +169,21 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
||||||
|
|
||||||
XmppStream? stream = stream_interactor.get_stream(account);
|
XmppStream? stream = stream_interactor.get_stream(account);
|
||||||
Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message);
|
Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message);
|
||||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
|
||||||
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||||
if (mam_message_flag != null && mam_flag != null && mam_flag.ns_ver == Xmpp.MessageArchiveManagement.NS_URI_2 && mam_message_flag.mam_id != null) {
|
if (mam_message_flag != null && mam_message_flag.mam_id != null) {
|
||||||
new_message.server_id = mam_message_flag.mam_id;
|
bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI);
|
||||||
|
if (server_does_mam) {
|
||||||
|
new_message.server_id = mam_message_flag.mam_id;
|
||||||
|
}
|
||||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
|
} else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
|
||||||
bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||||
(yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
(yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||||
if (server_supports_sid) {
|
if (server_supports_sid) {
|
||||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
|
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid);
|
||||||
}
|
}
|
||||||
} else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
|
} else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) {
|
||||||
bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||||
(yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
(yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||||
if (server_supports_sid) {
|
if (server_supports_sid) {
|
||||||
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
|
new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid);
|
||||||
}
|
}
|
||||||
|
@ -363,29 +364,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MamMessageListener : MessageListener {
|
|
||||||
|
|
||||||
public string[] after_actions_const = new string[]{ "DEDUPLICATE" };
|
|
||||||
public override string action_group { get { return "MAM_NODE"; } }
|
|
||||||
public override string[] after_actions { get { return after_actions_const; } }
|
|
||||||
|
|
||||||
private StreamInteractor stream_interactor;
|
|
||||||
|
|
||||||
public MamMessageListener(StreamInteractor stream_interactor) {
|
|
||||||
this.stream_interactor = stream_interactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
|
|
||||||
bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null;
|
|
||||||
XmppStream? stream = stream_interactor.get_stream(conversation.account);
|
|
||||||
Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null;
|
|
||||||
if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) {
|
|
||||||
conversation.account.mam_earliest_synced = message.local_time;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entities.Message create_out_message(string text, Conversation conversation) {
|
public Entities.Message create_out_message(string text, Conversation conversation) {
|
||||||
Entities.Message message = new Entities.Message(text);
|
Entities.Message message = new Entities.Message(text);
|
||||||
message.type_ = Util.get_message_type_for_conversation(conversation);
|
message.type_ = Util.get_message_type_for_conversation(conversation);
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class MucManager : StreamInteractionModule, Object {
|
||||||
|
|
||||||
bool receive_history = true;
|
bool receive_history = true;
|
||||||
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||||
bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI_2);
|
bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI);
|
||||||
if (can_do_mam) {
|
if (can_do_mam) {
|
||||||
receive_history = false;
|
receive_history = false;
|
||||||
history_since = null;
|
history_since = null;
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class Dino.Reactions : StreamInteractionModule, Object {
|
||||||
// The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids)
|
// The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids)
|
||||||
var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||||
bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) ||
|
||||||
(entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2));
|
(entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI));
|
||||||
if (!server_supports_sid) return false;
|
if (!server_supports_sid) return false;
|
||||||
|
|
||||||
bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI);
|
bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI);
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace Dino {
|
||||||
if (self_word != null && (account.alias == null || account.alias.length == 0)) {
|
if (self_word != null && (account.alias == null || account.alias.length == 0)) {
|
||||||
return self_word;
|
return self_word;
|
||||||
}
|
}
|
||||||
|
if (account.alias != null && account.alias.length == 0) return null;
|
||||||
return account.alias;
|
return account.alias;
|
||||||
}
|
}
|
||||||
Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid);
|
Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid);
|
||||||
|
|
|
@ -396,17 +396,13 @@ box.dino-input-error .chat-input-status.input-status-highlight-once {
|
||||||
text-shadow: 0 0 2px black;
|
text-shadow: 0 0 2px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dino-call-window .call-header-background {
|
.dino-call-window .participant-header-bar {
|
||||||
background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0));
|
background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0));
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dino-call-window .participant-header-bar button {
|
.dino-call-window .participant-header-bar button:hover:not(.close) {
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dino-call-window .participant-header-bar button:hover {
|
|
||||||
background: rgba(255,255,255,0.15);
|
background: rgba(255,255,255,0.15);
|
||||||
border-color: rgba(255,255,255,0);
|
border-color: rgba(255,255,255,0);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -22,7 +22,12 @@ namespace Dino.Ui {
|
||||||
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
|
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
|
||||||
private ArrayList<string> participants = new ArrayList<string>();
|
private ArrayList<string> participants = new ArrayList<string>();
|
||||||
|
|
||||||
|
private EventControllerFocus this_focus_events = new EventControllerFocus();
|
||||||
|
private GestureClick this_gesture_events = new GestureClick() { touch_only=true, propagation_phase=Gtk.PropagationPhase.CAPTURE };
|
||||||
private EventControllerMotion this_motion_events = new EventControllerMotion();
|
private EventControllerMotion this_motion_events = new EventControllerMotion();
|
||||||
|
private double latest_motion_x = -1;
|
||||||
|
private double latest_motion_y = -1;
|
||||||
|
private const double MOTION_RELEVANCE_THRESHOLD = 2;
|
||||||
|
|
||||||
private int own_video_width = 150;
|
private int own_video_width = 150;
|
||||||
private int own_video_height = 100;
|
private int own_video_height = 100;
|
||||||
|
@ -54,9 +59,20 @@ namespace Dino.Ui {
|
||||||
this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE);
|
this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
((Widget) this).add_controller(this_motion_events);
|
((Widget) this).add_controller(this_motion_events);
|
||||||
this_motion_events.motion.connect(reveal_control_elements);
|
this_motion_events.motion.connect((x, y) => {
|
||||||
this_motion_events.enter.connect(reveal_control_elements);
|
if ((latest_motion_x - x).abs() <= MOTION_RELEVANCE_THRESHOLD && (latest_motion_y - y).abs() <= MOTION_RELEVANCE_THRESHOLD) return;
|
||||||
this_motion_events.leave.connect(reveal_control_elements);
|
|
||||||
|
latest_motion_x = x;
|
||||||
|
latest_motion_y = y;
|
||||||
|
reveal_control_elements();
|
||||||
|
});
|
||||||
|
|
||||||
|
((Widget) this).add_controller(this_focus_events);
|
||||||
|
this_focus_events.enter.connect(reveal_control_elements);
|
||||||
|
this_focus_events.leave.connect(reveal_control_elements);
|
||||||
|
|
||||||
|
((Widget) this).add_controller(this_gesture_events);
|
||||||
|
this_gesture_events.pressed.connect_after(reveal_control_elements);
|
||||||
|
|
||||||
this.notify["default-width"].connect(reveal_control_elements);
|
this.notify["default-width"].connect(reveal_control_elements);
|
||||||
this.notify["default-height"].connect(reveal_control_elements);
|
this.notify["default-height"].connect(reveal_control_elements);
|
||||||
|
|
|
@ -74,14 +74,11 @@ namespace Dino.Ui {
|
||||||
|
|
||||||
header_bar.show_title_buttons = is_highest_row;
|
header_bar.show_title_buttons = is_highest_row;
|
||||||
if (is_highest_row) {
|
if (is_highest_row) {
|
||||||
header_bar.add_css_class("call-header-background");
|
|
||||||
Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
|
Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
|
||||||
if (gtk_settings != null) {
|
if (gtk_settings != null) {
|
||||||
string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
|
string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
|
||||||
header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : "");
|
header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : "");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
header_bar.remove_css_class("call-header-background");
|
|
||||||
}
|
}
|
||||||
reveal_or_hide_controls();
|
reveal_or_hide_controls();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ public class View : Box {
|
||||||
chooser.emoji_picked.connect((emoji) => {
|
chooser.emoji_picked.connect((emoji) => {
|
||||||
chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
|
chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
|
||||||
});
|
});
|
||||||
|
chooser.closed.connect(do_focus);
|
||||||
|
|
||||||
emoji_button.set_popover(chooser);
|
emoji_button.set_popover(chooser);
|
||||||
|
|
||||||
file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file"));
|
file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file"));
|
||||||
|
|
|
@ -215,7 +215,12 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
||||||
}
|
}
|
||||||
if (new_width == active_caps_width) return;
|
if (new_width == active_caps_width) return;
|
||||||
int new_height = device_caps_height * new_width / device_caps_width;
|
int new_height = device_caps_height * new_width / device_caps_width;
|
||||||
Gst.Caps new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null);
|
Gst.Caps new_caps;
|
||||||
|
if (device_caps_framerate_den != 0) {
|
||||||
|
new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null);
|
||||||
|
} else {
|
||||||
|
new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, null);
|
||||||
|
}
|
||||||
double required_bitrate = get_target_bitrate(new_caps);
|
double required_bitrate = get_target_bitrate(new_caps);
|
||||||
debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate);
|
debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate);
|
||||||
if (bitrate < required_bitrate && new_width > active_caps_width) return;
|
if (bitrate < required_bitrate && new_width > active_caps_width) return;
|
||||||
|
@ -347,7 +352,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
||||||
if (media == "audio") {
|
if (media == "audio") {
|
||||||
return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1");
|
return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1");
|
||||||
} else if (media == "video" && device.caps.get_size() > 0) {
|
} else if (media == "video" && device.caps.get_size() > 0) {
|
||||||
int best_index = 0;
|
int best_index = -1;
|
||||||
Value? best_fraction = null;
|
Value? best_fraction = null;
|
||||||
int best_fps = 0;
|
int best_fps = 0;
|
||||||
int best_width = 0;
|
int best_width = 0;
|
||||||
|
@ -390,10 +395,25 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
|
||||||
best_fraction = best_fraction_now;
|
best_fraction = best_fraction_now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (best_index == -1) {
|
||||||
|
// No caps in first round, try without framerate
|
||||||
|
for (int i = 0; i < device.caps.get_size(); i++) {
|
||||||
|
unowned Gst.Structure? that = device.caps.get_structure(i);
|
||||||
|
if (!that.has_name("video/x-raw")) continue;
|
||||||
|
int width = 0, height = 0;
|
||||||
|
if (!that.has_field("width") || !that.get_int("width", out width)) continue;
|
||||||
|
if (!that.has_field("height") || !that.get_int("height", out height)) continue;
|
||||||
|
if (best_width < width || best_width == width && best_height < height) {
|
||||||
|
best_width = width;
|
||||||
|
best_height = height;
|
||||||
|
best_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Gst.Caps res = caps_copy_nth(device.caps, best_index);
|
Gst.Caps res = caps_copy_nth(device.caps, best_index);
|
||||||
unowned Gst.Structure? that = res.get_structure(0);
|
unowned Gst.Structure? that = res.get_structure(0);
|
||||||
Value framerate = that.get_value("framerate");
|
Value? framerate = that.get_value("framerate");
|
||||||
if (framerate.type() == typeof(Gst.ValueList)) {
|
if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) {
|
||||||
that.set_value("framerate", best_fraction);
|
that.set_value("framerate", best_fraction);
|
||||||
}
|
}
|
||||||
debug("Selected caps %s", res.to_string());
|
debug("Selected caps %s", res.to_string());
|
||||||
|
|
|
@ -428,11 +428,11 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
|
||||||
|
|
||||||
if (media == "video") {
|
if (media == "video") {
|
||||||
// Pick best FPS
|
// Pick best FPS
|
||||||
int max_fps = 0;
|
int max_fps = -1;
|
||||||
Device? max_fps_device = null;
|
Device? max_fps_device = null;
|
||||||
foreach (Device device in devices) {
|
foreach (Device device in devices) {
|
||||||
int fps = get_max_fps(device);
|
int fps = get_max_fps(device);
|
||||||
if (fps > max_fps) {
|
if (fps > max_fps || max_fps_device == null) {
|
||||||
max_fps = fps;
|
max_fps = fps;
|
||||||
max_fps_device = device;
|
max_fps_device = device;
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
||||||
send_rtp.drop = true;
|
send_rtp.drop = true;
|
||||||
send_rtp.wait_on_eos = false;
|
send_rtp.wait_on_eos = false;
|
||||||
send_rtp.new_sample.connect(on_new_sample);
|
send_rtp.new_sample.connect(on_new_sample);
|
||||||
|
#if GST_1_20
|
||||||
|
send_rtp.new_serialized_event.connect(on_new_event);
|
||||||
|
#endif
|
||||||
send_rtp.connect("signal::eos", on_eos_static, this);
|
send_rtp.connect("signal::eos", on_eos_static, this);
|
||||||
pipe.add(send_rtp);
|
pipe.add(send_rtp);
|
||||||
|
|
||||||
|
@ -294,6 +297,60 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool flip = false;
|
||||||
|
uint8 rotation = 0;
|
||||||
|
#if GST_1_20
|
||||||
|
private bool on_new_event(Gst.App.Sink sink) {
|
||||||
|
if (sink == null || sink != send_rtp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Gst.MiniObject obj = sink.try_pull_object(0);
|
||||||
|
if (obj.type == typeof(Gst.Event)) {
|
||||||
|
unowned Gst.TagList tags;
|
||||||
|
if (((Gst.Event)obj).type == Gst.EventType.TAG) {
|
||||||
|
((Gst.Event)obj).parse_tag(out tags);
|
||||||
|
Gst.Video.OrientationMethod orientation_method;
|
||||||
|
Gst.Video.Orientation.from_tag(tags, out orientation_method);
|
||||||
|
switch (orientation_method) {
|
||||||
|
case Gst.Video.OrientationMethod.IDENTITY:
|
||||||
|
case Gst.Video.OrientationMethod.VERT:
|
||||||
|
default:
|
||||||
|
rotation = 0;
|
||||||
|
break;
|
||||||
|
case Gst.Video.OrientationMethod.@90R:
|
||||||
|
case Gst.Video.OrientationMethod.UL_LR:
|
||||||
|
rotation = 1;
|
||||||
|
break;
|
||||||
|
case Gst.Video.OrientationMethod.@180:
|
||||||
|
case Gst.Video.OrientationMethod.HORIZ:
|
||||||
|
rotation = 2;
|
||||||
|
break;
|
||||||
|
case Gst.Video.OrientationMethod.@90L:
|
||||||
|
case Gst.Video.OrientationMethod.UR_LL:
|
||||||
|
rotation = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (orientation_method) {
|
||||||
|
case Gst.Video.OrientationMethod.IDENTITY:
|
||||||
|
case Gst.Video.OrientationMethod.@90R:
|
||||||
|
case Gst.Video.OrientationMethod.@180:
|
||||||
|
case Gst.Video.OrientationMethod.@90L:
|
||||||
|
default:
|
||||||
|
flip = false;
|
||||||
|
break;
|
||||||
|
case Gst.Video.OrientationMethod.VERT:
|
||||||
|
case Gst.Video.OrientationMethod.UL_LR:
|
||||||
|
case Gst.Video.OrientationMethod.HORIZ:
|
||||||
|
case Gst.Video.OrientationMethod.UR_LL:
|
||||||
|
flip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) {
|
private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) {
|
||||||
if (sink == null) {
|
if (sink == null) {
|
||||||
debug("Sink is null");
|
debug("Sink is null");
|
||||||
|
@ -323,6 +380,24 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GST_1_20
|
||||||
|
if (sink == send_rtp) {
|
||||||
|
Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation");
|
||||||
|
if (ext != null) {
|
||||||
|
buffer = (Gst.Buffer) buffer.make_writable();
|
||||||
|
Gst.RTP.Buffer rtp_buffer;
|
||||||
|
if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.WRITE, out rtp_buffer)) {
|
||||||
|
uint8[] extension_data = new uint8[1];
|
||||||
|
bool camera = false;
|
||||||
|
extension_data[0] = extension_data[0] | (rotation & 0x3);
|
||||||
|
if (flip) extension_data[0] = extension_data[0] | 0x4;
|
||||||
|
if (camera) extension_data[0] = extension_data[0] | 0x8;
|
||||||
|
rtp_buffer.add_extension_onebyte_header(ext.id, extension_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
prepare_local_crypto();
|
prepare_local_crypto();
|
||||||
|
|
||||||
uint8[] data;
|
uint8[] data;
|
||||||
|
@ -489,8 +564,8 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint16 previous_video_orientation_degree = uint16.MAX;
|
private uint16 previous_incoming_video_orientation_degree = uint16.MAX;
|
||||||
public signal void video_orientation_changed(uint16 degree);
|
public signal void incoming_video_orientation_changed(uint16 degree);
|
||||||
|
|
||||||
public override void on_recv_rtp_data(Bytes bytes) {
|
public override void on_recv_rtp_data(Bytes bytes) {
|
||||||
if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) {
|
if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) {
|
||||||
|
@ -545,9 +620,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream {
|
||||||
case 2: rotation_degree = 180; break;
|
case 2: rotation_degree = 180; break;
|
||||||
case 3: rotation_degree = 270; break;
|
case 3: rotation_degree = 270; break;
|
||||||
}
|
}
|
||||||
if (rotation_degree != previous_video_orientation_degree) {
|
if (rotation_degree != previous_incoming_video_orientation_degree) {
|
||||||
video_orientation_changed(rotation_degree);
|
incoming_video_orientation_changed(rotation_degree);
|
||||||
previous_video_orientation_degree = rotation_degree;
|
previous_incoming_video_orientation_degree = rotation_degree;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -723,7 +798,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
||||||
private Gee.List<Gst.Element> outputs = new ArrayList<Gst.Element>();
|
private Gee.List<Gst.Element> outputs = new ArrayList<Gst.Element>();
|
||||||
private Gst.Element output_tee;
|
private Gst.Element output_tee;
|
||||||
private Gst.Element rotate;
|
private Gst.Element rotate;
|
||||||
private ulong video_orientation_changed_handler;
|
private ulong incoming_video_orientation_changed_handler;
|
||||||
|
|
||||||
public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) {
|
public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) {
|
||||||
base(plugin, content);
|
base(plugin, content);
|
||||||
|
@ -731,7 +806,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void create() {
|
public override void create() {
|
||||||
video_orientation_changed_handler = video_orientation_changed.connect(on_video_orientation_changed);
|
incoming_video_orientation_changed_handler = incoming_video_orientation_changed.connect(on_video_orientation_changed);
|
||||||
plugin.pause();
|
plugin.pause();
|
||||||
rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid");
|
rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid");
|
||||||
pipe.add(rotate);
|
pipe.add(rotate);
|
||||||
|
@ -780,7 +855,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream {
|
||||||
output_tee.set_state(Gst.State.NULL);
|
output_tee.set_state(Gst.State.NULL);
|
||||||
pipe.remove(output_tee);
|
pipe.remove(output_tee);
|
||||||
output_tee = null;
|
output_tee = null;
|
||||||
disconnect(video_orientation_changed_handler);
|
disconnect(incoming_video_orientation_changed_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void add_output(Gst.Element element, Xmpp.Jid? participant) {
|
public override void add_output(Gst.Element element, Xmpp.Jid? participant) {
|
||||||
|
|
|
@ -227,9 +227,21 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWi
|
||||||
if (connected_device == null) return;
|
if (connected_device == null) return;
|
||||||
plugin.pause();
|
plugin.pause();
|
||||||
pipe.add(sink);
|
pipe.add(sink);
|
||||||
|
#if GST_1_20
|
||||||
|
prepare = Gst.parse_bin_from_description(@"videoflip video-direction=auto name=video_widget_$(id)_orientation ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
|
||||||
|
#else
|
||||||
prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
|
prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true);
|
||||||
|
#endif
|
||||||
prepare.name = @"video_widget_$(id)_prepare";
|
prepare.name = @"video_widget_$(id)_prepare";
|
||||||
prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
#if GST_1_20
|
||||||
|
if (prepare is Gst.Bin) {
|
||||||
|
((Gst.Bin) prepare).get_by_name(@"video_widget_$(id)_flip").get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed);
|
||||||
|
#if GST_1_20
|
||||||
|
}
|
||||||
|
#endif
|
||||||
pipe.add(prepare);
|
pipe.add(prepare);
|
||||||
connected_device_element = connected_device.link_source();
|
connected_device_element = connected_device.link_source();
|
||||||
connected_device_element.link(prepare);
|
connected_device_element.link(prepare);
|
||||||
|
|
|
@ -28,54 +28,10 @@ GENERATE_HEADER
|
||||||
set(C_HEADERS_SRC "")
|
set(C_HEADERS_SRC "")
|
||||||
set(C_HEADERS_TARGET "")
|
set(C_HEADERS_TARGET "")
|
||||||
|
|
||||||
if(NOT BUILD_LIBSIGNAL_IN_TREE)
|
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
||||||
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
# we'll have to check compatibility for every new release
|
||||||
# we'll have to check compatibility for every new release
|
# distro maintainers may update this dependency after compatibility tests
|
||||||
# distro maintainers may update this dependency after compatibility tests
|
find_package(SignalProtocol 2.3.2 REQUIRED)
|
||||||
find_package(SignalProtocol 2.3.2 REQUIRED)
|
|
||||||
else()
|
|
||||||
add_subdirectory(libsignal-protocol-c EXCLUDE_FROM_ALL)
|
|
||||||
set_property(TARGET curve25519 PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
||||||
set_property(TARGET protobuf-c PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
||||||
set_property(TARGET signal-protocol-c PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
||||||
|
|
||||||
set(SIGNAL_PROTOCOL_C_HEADERS
|
|
||||||
signal_protocol.h
|
|
||||||
signal_protocol_types.h
|
|
||||||
curve.h
|
|
||||||
hkdf.h
|
|
||||||
ratchet.h
|
|
||||||
protocol.h
|
|
||||||
session_state.h
|
|
||||||
session_record.h
|
|
||||||
session_pre_key.h
|
|
||||||
session_builder.h
|
|
||||||
session_cipher.h
|
|
||||||
key_helper.h
|
|
||||||
sender_key.h
|
|
||||||
sender_key_state.h
|
|
||||||
sender_key_record.h
|
|
||||||
group_session_builder.h
|
|
||||||
group_cipher.h
|
|
||||||
fingerprint.h
|
|
||||||
device_consistency.h
|
|
||||||
)
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/exports/signal")
|
|
||||||
|
|
||||||
foreach(f ${SIGNAL_PROTOCOL_C_HEADERS})
|
|
||||||
list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}")
|
|
||||||
list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal/${f}")
|
|
||||||
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/signal/${f}"
|
|
||||||
COMMAND
|
|
||||||
cp "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" "${CMAKE_BINARY_DIR}/exports/signal/${f}"
|
|
||||||
DEPENDS
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}"
|
|
||||||
COMMENT
|
|
||||||
Copy header file signal/${f}
|
|
||||||
)
|
|
||||||
endforeach(f)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h")
|
list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h")
|
||||||
list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal_helper.h")
|
list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal_helper.h")
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28
|
|
|
@ -58,7 +58,7 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
||||||
fields.add(field);
|
fields.add(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MessageArchiveManagement.create_base_query(stream, MessageArchiveManagement.NS_URI_2, mam_params.query_id, fields);
|
return MessageArchiveManagement.create_base_query(stream, 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) {
|
||||||
|
@ -67,14 +67,14 @@ namespace Xmpp.MessageArchiveManagement.V2 {
|
||||||
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id));
|
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable);
|
return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async QueryResult page_through_results(XmppStream stream, MamQueryParams mam_params, QueryResult prev_result, Cancellable? cancellable = null) {
|
public async QueryResult page_through_results(XmppStream stream, MamQueryParams mam_params, QueryResult prev_result, Cancellable? cancellable = null) {
|
||||||
var query_node = create_base_query(stream, mam_params);
|
var query_node = create_base_query(stream, mam_params);
|
||||||
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(prev_result.first));
|
query_node.put_node(ResultSetManagement.create_set_rsm_node_before(prev_result.first));
|
||||||
|
|
||||||
return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable);
|
return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,6 @@ using Xmpp.Xep;
|
||||||
namespace Xmpp.MessageArchiveManagement {
|
namespace Xmpp.MessageArchiveManagement {
|
||||||
|
|
||||||
public const string NS_URI = "urn:xmpp:mam:2";
|
public const string NS_URI = "urn:xmpp:mam:2";
|
||||||
public const string NS_URI_2 = "urn:xmpp:mam:2";
|
|
||||||
public const string NS_URI_1 = "urn:xmpp:mam:1";
|
|
||||||
|
|
||||||
public class QueryResult {
|
public class QueryResult {
|
||||||
public bool error { get; set; default=false; }
|
public bool error { get; set; default=false; }
|
||||||
|
@ -36,43 +34,33 @@ public class Module : XmppStreamModule {
|
||||||
|
|
||||||
private async void query_availability(XmppStream stream) {
|
private async void query_availability(XmppStream stream) {
|
||||||
Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid;
|
Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid;
|
||||||
|
bool mam_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI);
|
||||||
bool ver_2_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI);
|
if (mam_available) {
|
||||||
if (ver_2_available) {
|
|
||||||
stream.add_flag(new Flag(NS_URI));
|
|
||||||
feature_available(stream);
|
feature_available(stream);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ver_1_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI_1);
|
|
||||||
if (ver_1_available) {
|
|
||||||
stream.add_flag(new Flag(NS_URI_1));
|
|
||||||
feature_available(stream);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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? queryid, Gee.List<DataForms.DataForm.Field> fields) {
|
||||||
DataForms.DataForm data_form = new DataForms.DataForm();
|
DataForms.DataForm data_form = new DataForms.DataForm();
|
||||||
|
|
||||||
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" };
|
||||||
form_type_field.set_value_string(NS_VER(stream));
|
form_type_field.set_value_string(NS_URI);
|
||||||
data_form.add_field(form_type_field);
|
data_form.add_field(form_type_field);
|
||||||
|
|
||||||
foreach (var field in fields) {
|
foreach (var field in fields) {
|
||||||
data_form.add_field(field);
|
data_form.add_field(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
StanzaNode query_node = new StanzaNode.build("query", NS_VER(stream)).add_self_xmlns().put_node(data_form.get_submit_node());
|
StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(data_form.get_submit_node());
|
||||||
query_node.put_attribute("queryid", queryid);
|
query_node.put_attribute("queryid", queryid);
|
||||||
return query_node;
|
return query_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async QueryResult query_archive(XmppStream stream, string ns, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) {
|
internal async QueryResult query_archive(XmppStream stream, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) {
|
||||||
|
|
||||||
var res = new QueryResult();
|
var res = new QueryResult();
|
||||||
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
Flag flag = Flag.get_flag(stream);
|
||||||
string? query_id = query_node.get_attribute("queryid");
|
string? query_id = query_node.get_attribute("queryid");
|
||||||
if (flag == null || query_id == null) { res.error = true; return res; }
|
if (flag == null || query_id == null) { res.error = true; return res; }
|
||||||
flag.active_query_ids.add(query_id);
|
flag.active_query_ids.add(query_id);
|
||||||
|
@ -83,7 +71,7 @@ public class Module : XmppStreamModule {
|
||||||
Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq, Priority.LOW, cancellable);
|
Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq, Priority.LOW, cancellable);
|
||||||
|
|
||||||
// Parse the response IQ into a QueryResult.
|
// Parse the response IQ into a QueryResult.
|
||||||
StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", ns);
|
StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", NS_URI);
|
||||||
if (fin_node == null) { res.malformed = true; return res; }
|
if (fin_node == null) { res.malformed = true; return res; }
|
||||||
|
|
||||||
StanzaNode? rsm_node = fin_node.get_subnode("set", Xmpp.ResultSetManagement.NS_URI);
|
StanzaNode? rsm_node = fin_node.get_subnode("set", Xmpp.ResultSetManagement.NS_URI);
|
||||||
|
@ -92,7 +80,7 @@ public class Module : XmppStreamModule {
|
||||||
res.first = rsm_node.get_deep_string_content("first");
|
res.first = rsm_node.get_deep_string_content("first");
|
||||||
res.last = rsm_node.get_deep_string_content("last");
|
res.last = rsm_node.get_deep_string_content("last");
|
||||||
if ((res.first == null) != (res.last == null)) { res.malformed = true; return res; }
|
if ((res.first == null) != (res.last == null)) { res.malformed = true; return res; }
|
||||||
res.complete = fin_node.get_attribute_bool("complete", false, ns);
|
res.complete = fin_node.get_attribute_bool("complete", false, NS_URI);
|
||||||
|
|
||||||
Idle.add(() => {
|
Idle.add(() => {
|
||||||
flag.active_query_ids.remove(query_id);
|
flag.active_query_ids.remove(query_id);
|
||||||
|
@ -110,15 +98,14 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
|
||||||
public override string[] after_actions { get { return after_actions_const; } }
|
public override string[] after_actions { get { return after_actions_const; } }
|
||||||
|
|
||||||
public override async bool run(XmppStream stream, MessageStanza message) {
|
public override async bool run(XmppStream stream, MessageStanza message) {
|
||||||
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
Flag flag = Flag.get_flag(stream);
|
||||||
if (flag == null) return false;
|
|
||||||
|
|
||||||
StanzaNode? message_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message");
|
StanzaNode? message_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message");
|
||||||
if (message_node != null) {
|
if (message_node != null) {
|
||||||
StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay");
|
||||||
DateTime? datetime = DelayedDelivery.get_time_for_node(forward_node);
|
DateTime? datetime = DelayedDelivery.get_time_for_node(forward_node);
|
||||||
string? mam_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":id");
|
string? mam_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":id");
|
||||||
string? query_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":queryid");
|
string? query_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":queryid");
|
||||||
|
|
||||||
if (query_id == null) {
|
if (query_id == null) {
|
||||||
warning("Received MAM message without queryid from %s, ignoring", message.from.to_string());
|
warning("Received MAM message without queryid from %s, ignoring", message.from.to_string());
|
||||||
|
@ -154,10 +141,14 @@ public class Flag : XmppStreamFlag {
|
||||||
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "message_archive_management");
|
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "message_archive_management");
|
||||||
public bool cought_up { get; set; default=false; }
|
public bool cought_up { get; set; default=false; }
|
||||||
public Gee.Set<string> active_query_ids { get; set; default = new HashSet<string>(); }
|
public Gee.Set<string> active_query_ids { get; set; default = new HashSet<string>(); }
|
||||||
public string ns_ver;
|
|
||||||
|
|
||||||
public Flag(string ns_ver) {
|
public static Flag get_flag(XmppStream stream) {
|
||||||
this.ns_ver = ns_ver;
|
Flag? flag = stream.get_flag(Flag.IDENTITY);
|
||||||
|
if (flag == null) {
|
||||||
|
flag = new Flag();
|
||||||
|
stream.add_flag(flag);
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string get_ns() { return NS_URI; }
|
public override string get_ns() { return NS_URI; }
|
||||||
|
@ -185,8 +176,4 @@ public class MessageFlag : Xmpp.MessageFlag {
|
||||||
public override string get_id() { return ID; }
|
public override string get_id() { return ID; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string NS_VER(XmppStream stream) {
|
|
||||||
return stream.get_flag(Flag.IDENTITY).ns_ver;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue