diff --git a/libdino/src/service/call_peer_state.vala b/libdino/src/service/call_peer_state.vala index cf39eec4..c97aa48d 100644 --- a/libdino/src/service/call_peer_state.vala +++ b/libdino/src/service/call_peer_state.vala @@ -90,7 +90,10 @@ public class Dino.PeerState : Object { } stream.get_module(Xmpp.Xep.JingleMessageInitiation.Module.IDENTITY).send_session_propose_to_peer(stream, jid, sid, descriptions); -// call_state.cim_call_id = stream.get_module(Xmpp.Xep.CallInvites.Module.IDENTITY).send_jingle_propose(stream, jid, sid, we_should_send_video); + +// Uncomment this use CIM instead of JMI +// call_state.cim_call_id = sid; +// stream.get_module(Xmpp.Xep.CallInvites.Module.IDENTITY).send_jingle_propose(stream, call_state.cim_call_id, jid, sid, we_should_send_video); } else if (jid_for_direct != null) { yield call_resource(jid_for_direct); } @@ -111,8 +114,27 @@ public class Dino.PeerState : Object { } public void accept() { + if (!call_state.accepted) { + critical("Tried to accept peer in unaccepted call?! Something's fishy. Abort."); + return; + } + if (session != null) { foreach (Xep.Jingle.Content content in session.contents) { + Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; + if (rtp_content_parameter != null && rtp_content_parameter.media == "video") { + // We didn't accept video but our peer wants to negotiate that content + if (!we_should_send_video && session.senders_include_us(content.senders)) { + if (session.senders_include_counterpart(content.senders)) { + // If our peer wants to send, let them + content.modify(session.we_initiated ? Xep.Jingle.Senders.RESPONDER : Xep.Jingle.Senders.INITIATOR); + } else { + // If only we're supposed to send, reject + content.reject(); + continue; + } + } + } content.accept(); } } else { diff --git a/libdino/src/service/call_state.vala b/libdino/src/service/call_state.vala index 73b26650..c403fc6a 100644 --- a/libdino/src/service/call_state.vala +++ b/libdino/src/service/call_state.vala @@ -373,7 +373,7 @@ public class Dino.CallState : Object { handle_peer_left(peer_state, false, Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC"); }); - if (group_call.peers_to_connect_to.size > 3) { + if (group_call.peers_to_connect_to.size > 4) { end("Call too full - P2p calls don't work well with many participants"); return; } diff --git a/libdino/src/service/calls.vala b/libdino/src/service/calls.vala index 443c9ae4..ef853903 100644 --- a/libdino/src/service/calls.vala +++ b/libdino/src/service/calls.vala @@ -159,7 +159,7 @@ namespace Dino { debug("[%s] Incoming call from %s from MUJI muc %s", account.bare_jid.to_string(), session.peer_full_jid.to_string(), muji_muc.to_string()); foreach (CallState call_state in call_states.values) { - if (call_state.group_call != null && call_state.group_call.muc_jid.equals(muji_muc)) { + if (call_state.call.account.equals(account) && call_state.group_call != null && call_state.group_call.muc_jid.equals(muji_muc)) { if (call_state.peers.keys.contains(session.peer_full_jid)) { PeerState peer_state = call_state.peers[session.peer_full_jid]; debug("[%s] Incoming call, we know the peer. Expected %s", account.bare_jid.to_string(), peer_state.waiting_for_inbound_muji_connection.to_string()); @@ -182,28 +182,19 @@ namespace Dino { debug(@"[%s] Incoming call from %s", account.bare_jid.to_string(), session.peer_full_jid.to_string()); - // Check if we already accepted this call via Jingle Message Initiation => accept - Call? call = null; - foreach (PeerState peer_state in jmi_request_peer.values) { - CallState call_state = call_states[peer_state.call]; - if (peer_state.sid == session.sid && - call_state.call.account.equals(account) && - peer_state.jid.equals_bare(session.peer_full_jid) && - call_state.we_should_send_video == counterpart_wants_video && - call_state.accepted) { - call = peer_state.call; - break; - } - } - if (call != null) { - jmi_request_peer[call].set_session(session); - jmi_request_peer[call].accept(); - jmi_request_peer.unset(call); + // Check if we already got this call via Jingle Message Initiation => accept + // PeerState.accept() checks if the call was accepted and ensures that we don't accidentally send video + PeerState? peer_state = get_peer_by_sid(account, session.sid, session.peer_full_jid); + if (peer_state != null) { + jmi_request_peer[peer_state.call].set_session(session); + jmi_request_peer[peer_state.call].accept(); + jmi_request_peer.unset(peer_state.call); return; } // This is a direct call without prior JMI. Ask user. - PeerState peer_state = create_received_call(account, session.peer_full_jid, account.full_jid, counterpart_wants_video); + if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(session.peer_full_jid.bare_jid, account)) return; + peer_state = create_received_call(account, session.peer_full_jid, account.full_jid, counterpart_wants_video); peer_state.set_session(session); Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(peer_state.call.counterpart.bare_jid, account, Conversation.Type.CHAT); call_incoming(peer_state.call, peer_state.call_state, conversation, counterpart_wants_video, false); @@ -260,8 +251,8 @@ namespace Dino { return null; } - private PeerState? get_peer_by_sid(Account account, string sid, Jid jid1, Jid jid2) { - Jid relevant_jid = jid1.equals_bare(account.bare_jid) ? jid2 : jid1; + private PeerState? get_peer_by_sid(Account account, string sid, Jid jid1, Jid? jid2 = null) { + Jid relevant_jid = jid1.equals_bare(account.bare_jid) && jid2 != null ? jid2 : jid1; foreach (CallState call_state in call_states.values) { if (!call_state.call.account.equals(account)) continue; @@ -284,7 +275,7 @@ namespace Dino { CallState call_state = call_states[call]; - if (call.counterparts.contains(inviter_jid) && call_state.accepted) { + if (call.counterparts.size == 1 && call.counterparts.contains(inviter_jid) && call_state.accepted) { // A call is converted into a group call. call_state.join_group_call.begin(muc_jid); return null; diff --git a/main/data/file_default_widget.ui b/main/data/file_default_widget.ui index 7da52ec2..6315e230 100644 --- a/main/data/file_default_widget.ui +++ b/main/data/file_default_widget.ui @@ -90,7 +90,8 @@ - False + True + 0 none diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala index dc952b30..28b7d477 100644 --- a/main/src/ui/conversation_content_view/file_default_widget.vala +++ b/main/src/ui/conversation_content_view/file_default_widget.vala @@ -85,7 +85,7 @@ public class FileDefaultWidget : EventBox { image_stack.set_visible_child_name("download_image"); } if (state == FileTransfer.State.COMPLETE) { - file_menu.visible = true; + file_menu.opacity = 1; } return false; } @@ -104,7 +104,7 @@ public class FileDefaultWidget : EventBox { if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("content_type_image"); } - file_menu.visible = false; + file_menu.opacity = 0; } private static string get_file_icon_name(string? mime_type) { diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala index 9969f507..56d50210 100644 --- a/xmpp-vala/src/module/xep/0045_muc/module.vala +++ b/xmpp-vala/src/module/xep/0045_muc/module.vala @@ -345,6 +345,12 @@ public class Module : XmppStreamModule { if (status_codes.contains(StatusCode.SELF_PRESENCE)) { Jid bare_jid = presence.from.bare_jid; if (flag.get_enter_id(bare_jid) != null) { + + // TODO only query that if we actually have the rights to + query_affiliation.begin(stream, bare_jid, "member"); + query_affiliation.begin(stream, bare_jid, "admin"); + query_affiliation.begin(stream, bare_jid, "owner"); + flag.finish_muc_enter(bare_jid); var join_result = new JoinResult() { nick=presence.from.resourcepart, newly_created=status_codes.contains(StatusCode.NEW_ROOM_CREATED) }; flag.enter_futures[bare_jid].set_value(join_result); diff --git a/xmpp-vala/src/module/xep/0353_call_invite_message.vala b/xmpp-vala/src/module/xep/0353_call_invite_message.vala index bc74ba2c..38d87f43 100644 --- a/xmpp-vala/src/module/xep/0353_call_invite_message.vala +++ b/xmpp-vala/src/module/xep/0353_call_invite_message.vala @@ -12,10 +12,10 @@ namespace Xmpp.Xep.CallInvites { public signal void call_rejected(Jid from, Jid to, string call_id, string message_type); public signal void call_left(Jid from, Jid to, string call_id, string message_type); - public string send_jingle_propose(XmppStream stream, Jid invitee, string sid, bool video) { + public void send_jingle_propose(XmppStream stream, string call_id, Jid invitee, string sid, bool video) { StanzaNode jingle_node = new StanzaNode.build("jingle", CallInvites.NS_URI) .put_attribute("sid", sid); - return send_propose(stream, sid, invitee, jingle_node, video, false, MessageStanza.TYPE_CHAT); + send_propose(stream, call_id, invitee, jingle_node, video, false, MessageStanza.TYPE_CHAT); } public void send_muji_propose(XmppStream stream, string call_id, Jid invitee, Jid muc_jid, bool video, string message_type) { @@ -24,7 +24,7 @@ namespace Xmpp.Xep.CallInvites { send_propose(stream, call_id, invitee, muji_node, video, true, message_type); } - private string send_propose(XmppStream stream, string call_id, Jid invitee, StanzaNode inner_node, bool video, bool multiparty, string message_type) { + private void send_propose(XmppStream stream, string call_id, Jid invitee, StanzaNode inner_node, bool video, bool multiparty, string message_type) { StanzaNode invite_node = new StanzaNode.build("propose", NS_URI).add_self_xmlns() .put_attribute("id", call_id) .put_attribute("video", video.to_string()) @@ -34,7 +34,6 @@ namespace Xmpp.Xep.CallInvites { MessageProcessingHints.set_message_hint(invite_message, MessageProcessingHints.HINT_STORE); invite_message.stanza.put_node(invite_node); stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, invite_message); - return invite_message.id; } public void send_retract(XmppStream stream, Jid to, string call_id, string message_type) { @@ -81,6 +80,12 @@ namespace Xmpp.Xep.CallInvites { if (relevant_node.name == "propose") { if (relevant_node.sub_nodes.is_empty) return; + + // If there's also a JMI node, just use that one instead. + foreach (StanzaNode node in message.stanza.sub_nodes) { + if (node.ns_uri == JingleMessageInitiation.NS_URI) return; + } + bool video = relevant_node.get_attribute_bool("video", false); call_proposed(message.from, message.to, call_id, video, relevant_node.sub_nodes, message); return;