aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdino/src/entity/call.vala13
-rw-r--r--libdino/src/service/call_state.vala39
-rw-r--r--libdino/src/service/calls.vala62
-rw-r--r--main/src/ui/conversation_titlebar/call_entry.vala18
-rw-r--r--xmpp-vala/src/module/xep/muji_meta.vala36
5 files changed, 116 insertions, 52 deletions
diff --git a/libdino/src/entity/call.vala b/libdino/src/entity/call.vala
index 5221a807..3b48f664 100644
--- a/libdino/src/entity/call.vala
+++ b/libdino/src/entity/call.vala
@@ -39,11 +39,6 @@ namespace Dino.Entities {
id = row[db.call.id];
account = db.get_account_by_id(row[db.call.account_id]);
- counterpart = db.get_jid_by_id(row[db.call.counterpart_id]);
- string counterpart_resource = row[db.call.counterpart_resource];
- if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource);
- counterparts.add(counterpart);
-
string our_resource = row[db.call.our_resource];
if (our_resource != null) {
ourpart = account.bare_jid.with_resource(our_resource);
@@ -66,6 +61,14 @@ namespace Dino.Entities {
if (counterpart == null) counterpart = peer;
}
+ counterpart = db.get_jid_by_id(row[db.call.counterpart_id]);
+ string counterpart_resource = row[db.call.counterpart_resource];
+ if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource);
+ if (counterparts.is_empty) {
+ counterparts.add(counterpart);
+ counterpart = counterpart;
+ }
+
notify.connect(on_update);
}
diff --git a/libdino/src/service/call_state.vala b/libdino/src/service/call_state.vala
index 385cf9dc..51563552 100644
--- a/libdino/src/service/call_state.vala
+++ b/libdino/src/service/call_state.vala
@@ -11,6 +11,7 @@ public class Dino.CallState : Object {
public StreamInteractor stream_interactor;
public Call call;
public Xep.Muji.GroupCall? group_call { get; set; }
+ public Jid? parent_muc { get; set; }
public Jid? invited_to_group_call = null;
public Jid? group_call_inviter = null;
public bool accepted { get; private set; default=false; }
@@ -19,6 +20,8 @@ public class Dino.CallState : Object {
public bool we_should_send_video { get; set; default=false; }
public HashMap<Jid, PeerState> peers = new HashMap<Jid, PeerState>(Jid.hash_func, Jid.equals_func);
+ private string message_type = Xmpp.MessageStanza.TYPE_CHAT;
+
public CallState(Call call, StreamInteractor stream_interactor) {
this.call = call;
this.stream_interactor = stream_interactor;
@@ -37,6 +40,29 @@ public class Dino.CallState : Object {
}
}
+ internal async void initiate_groupchat_call(Jid muc) {
+ parent_muc = muc;
+ message_type = Xmpp.MessageStanza.TYPE_GROUPCHAT;
+
+ if (this.group_call == null) yield convert_into_group_call();
+ if (this.group_call == null) return;
+ // The user might have retracted the call in the meanwhile
+ if (this.call.state != Call.State.RINGING) return;
+
+ XmppStream stream = stream_interactor.get_stream(call.account);
+ if (stream == null) return;
+
+ Gee.List<Jid> occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(muc, call.account);
+ foreach (Jid occupant in occupants) {
+ Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, call.account);
+ if (real_jid == null) continue;
+ debug(@"Adding MUC member as MUJI MUC owner %s", real_jid.bare_jid.to_string());
+ yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, real_jid.bare_jid, null, "owner");
+ }
+
+ stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite(stream, muc, group_call.muc_jid, we_should_send_video, message_type);
+ }
+
internal PeerState set_first_peer(Jid peer) {
var peer_state = new PeerState(peer, call, stream_interactor);
peer_state.first_peer = true;
@@ -57,7 +83,7 @@ public class Dino.CallState : Object {
if (invited_to_group_call != null) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
- stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_accept_to_peer(stream, group_call_inviter, invited_to_group_call);
+ stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_accept_to_peer(stream, group_call_inviter, invited_to_group_call, message_type);
join_group_call.begin(invited_to_group_call);
} else {
foreach (PeerState peer in peers.values) {
@@ -73,7 +99,7 @@ public class Dino.CallState : Object {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_reject_to_self(stream, invited_to_group_call);
- stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_reject_to_peer(stream, group_call_inviter, invited_to_group_call);
+ stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_reject_to_peer(stream, group_call_inviter, invited_to_group_call, message_type);
}
var peers_cpy = new ArrayList<PeerState>();
peers_cpy.add_all(peers.values);
@@ -87,6 +113,10 @@ public class Dino.CallState : Object {
var peers_cpy = new ArrayList<PeerState>();
peers_cpy.add_all(peers.values);
+ if (group_call != null) {
+ stream_interactor.get_module(MucManager.IDENTITY).part(call.account, group_call.muc_jid);
+ }
+
if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.SUCCESS);
@@ -96,6 +126,11 @@ public class Dino.CallState : Object {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.CANCEL);
}
+ if (parent_muc != null) {
+ XmppStream stream = stream_interactor.get_stream(call.account);
+ if (stream == null) return;
+ stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_retract_to_peer(stream, parent_muc, group_call.muc_jid, message_type);
+ }
call.state = Call.State.MISSED;
} else {
return;
diff --git a/libdino/src/service/calls.vala b/libdino/src/service/calls.vala
index 3d1ed7e8..629d8074 100644
--- a/libdino/src/service/calls.vala
+++ b/libdino/src/service/calls.vala
@@ -39,7 +39,8 @@ namespace Dino {
Call call = new Call();
call.direction = Call.DIRECTION_OUTGOING;
call.account = conversation.account;
- call.add_peer(conversation.counterpart);
+ // TODO we should only do that for Conversation.Type.CHAT, but the database currently requires a counterpart from the start
+ call.counterpart = conversation.counterpart;
call.ourpart = conversation.account.full_jid;
call.time = call.local_time = call.end_time = new DateTime.now_utc();
call.state = Call.State.RINGING;
@@ -50,9 +51,14 @@ namespace Dino {
call_state.we_should_send_video = video;
call_state.we_should_send_audio = true;
connect_call_state_signals(call_state);
- PeerState peer_state = call_state.set_first_peer(conversation.counterpart);
- yield peer_state.initiate_call(conversation.counterpart);
+ if (conversation.type_ == Conversation.Type.CHAT) {
+ call.add_peer(conversation.counterpart);
+ PeerState peer_state = call_state.set_first_peer(conversation.counterpart);
+ yield peer_state.initiate_call(conversation.counterpart);
+ } else {
+ call_state.initiate_groupchat_call(conversation.counterpart);
+ }
conversation.last_active = call.time;
@@ -63,7 +69,12 @@ namespace Dino {
public async bool can_do_audio_calls_async(Conversation conversation) {
if (!can_do_audio_calls()) return false;
- return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
+
+ if (conversation.type_ == Conversation.Type.CHAT) {
+ return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
+ } else {
+ return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
+ }
}
private bool can_do_audio_calls() {
@@ -75,7 +86,12 @@ namespace Dino {
public async bool can_do_video_calls_async(Conversation conversation) {
if (!can_do_video_calls()) return false;
- return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
+
+ if (conversation.type_ == Conversation.Type.CHAT) {
+ return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
+ } else {
+ return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
+ }
}
private bool can_do_video_calls() {
@@ -237,17 +253,24 @@ namespace Dino {
private CallState? get_call_state_for_groupcall(Account account, Jid muc_jid) {
foreach (CallState call_state in call_states.values) {
- if (call_state.group_call != null && call_state.call.account.equals(account) && call_state.group_call.muc_jid.equals(muc_jid)) {
- return call_state;
- }
+ if (!call_state.call.account.equals(account)) continue;
+
+ if (call_state.group_call != null && call_state.group_call.muc_jid.equals(muc_jid)) return call_state;
+ if (call_state.invited_to_group_call != null && call_state.invited_to_group_call.equals(muc_jid)) return call_state;
}
return null;
}
- private async void on_muji_call_received(Account account, Jid inviter_jid, Jid muc_jid, Gee.List<StanzaNode> descriptions) {
- debug("[%s] on_muji_call_received", account.bare_jid.to_string());
+ private async void on_muji_call_received(Account account, Jid inviter_jid, Jid muc_jid, Gee.List<StanzaNode> descriptions, string message_type) {
+ debug("[%s] Muji call received from %s for MUC %s, type %s", account.bare_jid.to_string(), inviter_jid.to_string(), muc_jid.to_string(), message_type);
+
foreach (Call call in call_states.keys) {
- if (call.account.equals(account) && call.counterparts.contains(inviter_jid) && call_states[call].accepted) {
+ if (!call.account.equals(account)) return;
+
+ // We already know the call; this is a reflection of our own invite
+ if (call_states[call].parent_muc.equals_bare(inviter_jid)) return;
+
+ if (call.counterparts.contains(inviter_jid) && call_states[call].accepted) {
// A call is converted into a group call.
yield call_states[call].join_group_call(muc_jid);
return;
@@ -373,11 +396,11 @@ namespace Dino {
});
Xep.MujiMeta.Module muji_meta_module = stream_interactor.module_manager.get_module(account, Xep.MujiMeta.Module.IDENTITY);
- muji_meta_module.call_proposed.connect((inviter_jid, to, muc_jid, descriptions) => {
+ muji_meta_module.call_proposed.connect((inviter_jid, to, muc_jid, descriptions, message_type) => {
if (inviter_jid.equals_bare(account.bare_jid)) return;
- on_muji_call_received.begin(account, inviter_jid, muc_jid, descriptions);
+ on_muji_call_received.begin(account, inviter_jid, muc_jid, descriptions, message_type);
});
- muji_meta_module.call_accepted.connect((from_jid, muc_jid) => {
+ muji_meta_module.call_accepted.connect((from_jid, muc_jid, message_type) => {
if (!from_jid.equals_bare(account.bare_jid)) return;
// We accepted the call from another device
@@ -387,17 +410,24 @@ namespace Dino {
call_state.call.state = Call.State.OTHER_DEVICE;
remove_call_from_datastructures(call_state.call);
});
- muji_meta_module.call_retracted.connect((from_jid, muc_jid) => {
+ muji_meta_module.call_retracted.connect((from_jid, to_jid, muc_jid, message_type) => {
if (from_jid.equals_bare(account.bare_jid)) return;
// The call was retracted by the counterpart
CallState? call_state = get_call_state_for_groupcall(account, muc_jid);
if (call_state == null) return;
+ if (call_state.call.state != Call.State.RINGING) {
+ debug("%s tried to retract a call that's in state %s. Ignoring.", from_jid.to_string(), call_state.call.state.to_string());
+ return;
+ }
+
+ // TODO prevent other MUC occupants from retracting a call
+
call_state.call.state = Call.State.MISSED;
remove_call_from_datastructures(call_state.call);
});
- muji_meta_module.call_rejected.connect((from_jid, to_jid, muc_jid) => {
+ muji_meta_module.call_rejected.connect((from_jid, to_jid, muc_jid, message_type) => {
if (from_jid.equals_bare(account.bare_jid)) return;
debug(@"[%s] rejected our MUJI invite to %s", account.bare_jid.to_string(), from_jid.to_string(), muc_jid.to_string());
});
diff --git a/main/src/ui/conversation_titlebar/call_entry.vala b/main/src/ui/conversation_titlebar/call_entry.vala
index 3b3a5b39..3fa399a6 100644
--- a/main/src/ui/conversation_titlebar/call_entry.vala
+++ b/main/src/ui/conversation_titlebar/call_entry.vala
@@ -115,17 +115,13 @@ namespace Dino.Ui {
return;
}
- if (conversation.type_ == Conversation.Type.CHAT) {
- Conversation conv_bak = conversation;
- bool audio_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_audio_calls_async(conversation);
- bool video_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_video_calls_async(conversation);
- if (conv_bak != conversation) return;
-
- visible = audio_works;
- video_button.visible = video_works;
- } else {
- visible = false;
- }
+ Conversation conv_bak = conversation;
+ bool audio_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_audio_calls_async(conversation);
+ bool video_works = yield stream_interactor.get_module(Calls.IDENTITY).can_do_video_calls_async(conversation);
+ if (conv_bak != conversation) return;
+
+ visible = audio_works;
+ video_button.visible = video_works;
}
public new void unset_conversation() { }
diff --git a/xmpp-vala/src/module/xep/muji_meta.vala b/xmpp-vala/src/module/xep/muji_meta.vala
index 4452e611..89a0e8de 100644
--- a/xmpp-vala/src/module/xep/muji_meta.vala
+++ b/xmpp-vala/src/module/xep/muji_meta.vala
@@ -6,48 +6,48 @@ namespace Xmpp.Xep.MujiMeta {
public class Module : XmppStreamModule {
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "muji_meta");
- public signal void call_proposed(Jid from, Jid to, Jid muc_jid, Gee.List<StanzaNode> descriptions);
- public signal void call_retracted(Jid from, Jid to, Jid muc_jid);
- public signal void call_accepted(Jid from, Jid muc_jid);
- public signal void call_rejected(Jid from, Jid to, Jid muc_jid);
+ public signal void call_proposed(Jid from, Jid to, Jid muc_jid, Gee.List<StanzaNode> descriptions, string message_type);
+ public signal void call_retracted(Jid from, Jid to, Jid muc_jid, string message_type);
+ public signal void call_accepted(Jid from, Jid muc_jid, string message_type);
+ public signal void call_rejected(Jid from, Jid to, Jid muc_jid, string message_type);
- public void send_invite(XmppStream stream, Jid invitee, Jid muc_jid, bool video) {
+ public void send_invite(XmppStream stream, Jid invitee, Jid muc_jid, bool video, string? message_type = null) {
var invite_node = new StanzaNode.build("propose", NS_URI).put_attribute("muc", muc_jid.to_string());
invite_node.put_node(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "audio"));
if (video) {
invite_node.put_node(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "video"));
}
var muji_node = new StanzaNode.build("muji", NS_URI).add_self_xmlns().put_node(invite_node);
- MessageStanza invite_message = new MessageStanza() { to=invitee, type_=MessageStanza.TYPE_CHAT };
+ MessageStanza invite_message = new MessageStanza() { to=invitee, type_=message_type };
invite_message.stanza.put_node(muji_node);
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, invite_message);
}
- public void send_invite_retract_to_peer(XmppStream stream, Jid invitee, Jid muc_jid) {
- send_jmi_message(stream, "retract", invitee, muc_jid);
+ public void send_invite_retract_to_peer(XmppStream stream, Jid invitee, Jid muc_jid, string? message_type = null) {
+ send_jmi_message(stream, "retract", invitee, muc_jid, message_type);
}
- public void send_invite_accept_to_peer(XmppStream stream, Jid invitor, Jid muc_jid) {
- send_jmi_message(stream, "accept", invitor, muc_jid);
+ public void send_invite_accept_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string? message_type = null) {
+ send_jmi_message(stream, "accept", invitor, muc_jid, message_type);
}
public void send_invite_accept_to_self(XmppStream stream, Jid muc_jid) {
send_jmi_message(stream, "accept", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid);
}
- public void send_invite_reject_to_peer(XmppStream stream, Jid invitor, Jid muc_jid) {
- send_jmi_message(stream, "reject", invitor, muc_jid);
+ public void send_invite_reject_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string? message_type = null) {
+ send_jmi_message(stream, "reject", invitor, muc_jid, message_type);
}
public void send_invite_reject_to_self(XmppStream stream, Jid muc_jid) {
send_jmi_message(stream, "reject", Bind.Flag.get_my_jid(stream).bare_jid, muc_jid);
}
- private void send_jmi_message(XmppStream stream, string name, Jid to, Jid muc) {
+ private void send_jmi_message(XmppStream stream, string name, Jid to, Jid muc, string? message_type = null) {
var jmi_node = new StanzaNode.build(name, NS_URI).add_self_xmlns().put_attribute("muc", muc.to_string());
var muji_node = new StanzaNode.build("muji", NS_URI).add_self_xmlns().put_node(jmi_node);
- MessageStanza accepted_message = new MessageStanza() { to=to, type_=MessageStanza.TYPE_CHAT };
+ MessageStanza accepted_message = new MessageStanza() { to=to, type_= message_type ?? MessageStanza.TYPE_CHAT };
accepted_message.stanza.put_node(muji_node);
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, accepted_message);
}
@@ -80,7 +80,7 @@ namespace Xmpp.Xep.MujiMeta {
switch (mi_node.name) {
case "accept":
case "proceed":
- call_accepted(message.from, muc_jid);
+ call_accepted(message.from, muc_jid, message.type_);
break;
case "propose":
ArrayList<StanzaNode> descriptions = new ArrayList<StanzaNode>();
@@ -91,14 +91,14 @@ namespace Xmpp.Xep.MujiMeta {
}
if (descriptions.size > 0) {
- call_proposed(message.from, message.to, muc_jid, descriptions);
+ call_proposed(message.from, message.to, muc_jid, descriptions, message.type_);
}
break;
case "retract":
- call_retracted(message.from, message.to, muc_jid);
+ call_retracted(message.from, message.to, muc_jid, message.type_);
break;
case "reject":
- call_rejected(message.from, message.to, muc_jid);
+ call_rejected(message.from, message.to, muc_jid, message.type_);
break;
}
}