Add support for call invite messages
As of https://github.com/xsf/xeps/pull/1155
This commit is contained in:
parent
4ef50db3e5
commit
071d925e37
|
@ -15,6 +15,7 @@ public class Dino.CallState : Object {
|
||||||
public Jid? parent_muc { get; set; }
|
public Jid? parent_muc { get; set; }
|
||||||
public Jid? invited_to_group_call = null;
|
public Jid? invited_to_group_call = null;
|
||||||
public Jid? group_call_inviter = null;
|
public Jid? group_call_inviter = null;
|
||||||
|
public string? invite_id = null;
|
||||||
public bool accepted { get; private set; default=false; }
|
public bool accepted { get; private set; default=false; }
|
||||||
|
|
||||||
public bool we_should_send_audio { get; set; default=false; }
|
public bool we_should_send_audio { get; set; default=false; }
|
||||||
|
@ -61,7 +62,7 @@ public class Dino.CallState : Object {
|
||||||
yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, real_jid.bare_jid, null, "owner");
|
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);
|
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_invite(stream, muc, group_call.muc_jid, we_should_send_video, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal PeerState set_first_peer(Jid peer) {
|
internal PeerState set_first_peer(Jid peer) {
|
||||||
|
@ -84,7 +85,7 @@ public class Dino.CallState : Object {
|
||||||
if (invited_to_group_call != null) {
|
if (invited_to_group_call != null) {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
if (stream == null) return;
|
if (stream == null) return;
|
||||||
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_accept_to_peer(stream, group_call_inviter, invited_to_group_call, message_type);
|
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_accept(stream, group_call_inviter, invite_id, message_type);
|
||||||
join_group_call.begin(invited_to_group_call);
|
join_group_call.begin(invited_to_group_call);
|
||||||
} else {
|
} else {
|
||||||
foreach (PeerState peer in peers.values) {
|
foreach (PeerState peer in peers.values) {
|
||||||
|
@ -99,8 +100,7 @@ public class Dino.CallState : Object {
|
||||||
if (invited_to_group_call != null) {
|
if (invited_to_group_call != null) {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
if (stream == null) return;
|
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.CallInvites.Module.IDENTITY).send_reject(stream, invited_to_group_call, invite_id, message_type);
|
||||||
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>();
|
var peers_cpy = new ArrayList<PeerState>();
|
||||||
peers_cpy.add_all(peers.values);
|
peers_cpy.add_all(peers.values);
|
||||||
|
@ -133,7 +133,7 @@ public class Dino.CallState : Object {
|
||||||
if (parent_muc != null && group_call != null) {
|
if (parent_muc != null && group_call != null) {
|
||||||
XmppStream stream = stream_interactor.get_stream(call.account);
|
XmppStream stream = stream_interactor.get_stream(call.account);
|
||||||
if (stream == null) return;
|
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);
|
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, parent_muc, invite_id, message_type);
|
||||||
}
|
}
|
||||||
call.state = Call.State.MISSED;
|
call.state = Call.State.MISSED;
|
||||||
} else {
|
} else {
|
||||||
|
@ -172,9 +172,10 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string());
|
debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string());
|
||||||
yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner");
|
yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner");
|
||||||
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite(stream, invitee, group_call.muc_jid, we_should_send_video, message_type);
|
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_invite(stream, invitee, group_call.muc_jid, we_should_send_video, "chat");
|
||||||
|
|
||||||
// If the peer hasn't accepted within a minute, retract the invite
|
// If the peer hasn't accepted within a minute, retract the invite
|
||||||
|
// TODO this should be unset when we retract the invite. otherwise a second invite attempt might break due to this
|
||||||
Timeout.add_seconds(60, () => {
|
Timeout.add_seconds(60, () => {
|
||||||
if (this == null) return false;
|
if (this == null) return false;
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ public class Dino.CallState : Object {
|
||||||
|
|
||||||
if (!contains_peer) {
|
if (!contains_peer) {
|
||||||
debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string());
|
debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string());
|
||||||
stream.get_module(Xep.MujiMeta.Module.IDENTITY).send_invite_retract_to_peer(stream, invitee, group_call.muc_jid, message_type);
|
// stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, invitee, invite_id);
|
||||||
stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none");
|
stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -79,7 +79,10 @@ namespace Dino {
|
||||||
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
|
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
|
||||||
} else {
|
} else {
|
||||||
bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
|
bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
|
||||||
return is_private && can_initiate_groupcall(conversation.account);
|
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
|
||||||
|
bool supports_ussid = yield entity_info.has_feature(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI);
|
||||||
|
bool supports_mam = yield entity_info.has_feature(conversation.account, conversation.counterpart.bare_jid, Xep.MessageArchiveManagement.NS_URI_2);
|
||||||
|
return is_private && (supports_ussid || supports_mam) && can_initiate_groupcall(conversation.account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,35 +238,43 @@ namespace Dino {
|
||||||
return peer_state;
|
return peer_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallState? get_call_state_for_groupcall(Account account, Jid muc_jid) {
|
private CallState? get_call_state_by_invite_id(Account account, Jid peer_jid, string invite_id) {
|
||||||
foreach (CallState call_state in call_states.values) {
|
foreach (CallState call_state in call_states.values) {
|
||||||
if (!call_state.call.account.equals(account)) continue;
|
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.group_call != null && call_state.invite_id == invite_id) {
|
||||||
if (call_state.invited_to_group_call != null && call_state.invited_to_group_call.equals(muc_jid)) return call_state;
|
foreach (Jid jid in call_state.peers.keys) {
|
||||||
|
if (jid.equals(peer_jid)) {
|
||||||
|
return call_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (call_state.invited_to_group_call != null && call_state.invited_to_group_call.equals(peer_jid)) return call_state;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void on_muji_call_received(Account account, Jid inviter_jid, Jid muc_jid, Gee.List<StanzaNode> descriptions, string message_type) {
|
private async void on_muji_call_received(Account account, Jid inviter_jid, Jid muc_jid, string invite_id, bool video, 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);
|
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) {
|
foreach (Call call in call_states.keys) {
|
||||||
if (!call.account.equals(account)) return;
|
if (!call.account.equals(account)) return;
|
||||||
|
|
||||||
// We already know the call; this is a reflection of our own invite
|
CallState call_state = call_states[call];
|
||||||
if (call_states[call].parent_muc != null && call_states[call].parent_muc.equals_bare(inviter_jid)) return;
|
|
||||||
|
|
||||||
if (call.counterparts.contains(inviter_jid) && call_states[call].accepted) {
|
// If this is a MUC reflection of our own invite, store the sid assigned by the MUC
|
||||||
|
if (call_state.parent_muc != null && call_state.parent_muc.equals_bare(inviter_jid)) {
|
||||||
|
call_state.invite_id = invite_id;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (call.counterparts.contains(inviter_jid) && call_state.accepted) {
|
||||||
// A call is converted into a group call.
|
// A call is converted into a group call.
|
||||||
yield call_states[call].join_group_call(muc_jid);
|
yield call_state.join_group_call(muc_jid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio");
|
|
||||||
bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video");
|
|
||||||
|
|
||||||
Call call = new Call();
|
Call call = new Call();
|
||||||
call.direction = Call.DIRECTION_INCOMING;
|
call.direction = Call.DIRECTION_INCOMING;
|
||||||
call.ourpart = account.full_jid;
|
call.ourpart = account.full_jid;
|
||||||
|
@ -280,12 +291,13 @@ namespace Dino {
|
||||||
CallState call_state = new CallState(call, stream_interactor);
|
CallState call_state = new CallState(call, stream_interactor);
|
||||||
connect_call_state_signals(call_state);
|
connect_call_state_signals(call_state);
|
||||||
call_state.we_should_send_audio = true;
|
call_state.we_should_send_audio = true;
|
||||||
call_state.we_should_send_video = video_requested;
|
call_state.we_should_send_video = video;
|
||||||
call_state.invited_to_group_call = muc_jid;
|
call_state.invited_to_group_call = muc_jid;
|
||||||
call_state.group_call_inviter = inviter_jid;
|
call_state.group_call_inviter = inviter_jid;
|
||||||
|
call_state.invite_id = invite_id;
|
||||||
|
|
||||||
debug("[%s] on_muji_call_received accepting", account.bare_jid.to_string());
|
debug("[%s] on_muji_call_received accepting", account.bare_jid.to_string());
|
||||||
call_incoming(call_state.call, call_state, conversation, video_requested);
|
call_incoming(call_state.call, call_state, conversation, video);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remove_call_from_datastructures(Call call) {
|
private void remove_call_from_datastructures(Call call) {
|
||||||
|
@ -379,26 +391,43 @@ namespace Dino {
|
||||||
remove_call_from_datastructures(call);
|
remove_call_from_datastructures(call);
|
||||||
});
|
});
|
||||||
|
|
||||||
Xep.MujiMeta.Module muji_meta_module = stream_interactor.module_manager.get_module(account, Xep.MujiMeta.Module.IDENTITY);
|
Xep.CallInvites.Module call_invites_module = stream_interactor.module_manager.get_module(account, Xep.CallInvites.Module.IDENTITY);
|
||||||
muji_meta_module.call_proposed.connect((inviter_jid, to, muc_jid, descriptions, message_type) => {
|
call_invites_module.call_proposed.connect((from_jid, to_jid, video, join_methods, message_stanza) => {
|
||||||
if (inviter_jid.equals_bare(account.bare_jid)) return;
|
if (from_jid.equals_bare(account.bare_jid)) return;
|
||||||
on_muji_call_received.begin(account, inviter_jid, muc_jid, descriptions, message_type);
|
foreach (StanzaNode join_method_node in join_methods) {
|
||||||
|
if (join_method_node.ns_uri == Xep.Muji.NS_URI) {
|
||||||
|
string? room_jid_str = join_method_node.get_attribute("room");
|
||||||
|
if (room_jid_str == null) return;
|
||||||
|
Jid room_jid = new Jid(room_jid_str);
|
||||||
|
string? invite_id = null;
|
||||||
|
if (message_stanza.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) {
|
||||||
|
invite_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message_stanza, from_jid.bare_jid);
|
||||||
|
} else {
|
||||||
|
invite_id = message_stanza.id;
|
||||||
|
}
|
||||||
|
if (invite_id == null) {
|
||||||
|
warning("Got call invite without ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
on_muji_call_received.begin(account, from_jid, room_jid, id, video, message_stanza.type_);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
muji_meta_module.call_accepted.connect((from_jid, muc_jid, message_type) => {
|
call_invites_module.call_accepted.connect((from_jid, invite_id, message_type) => {
|
||||||
if (!from_jid.equals_bare(account.bare_jid)) return;
|
if (!from_jid.equals_bare(account.bare_jid)) return;
|
||||||
|
|
||||||
// We accepted the call from another device
|
// We accepted the call from another device
|
||||||
CallState? call_state = get_call_state_for_groupcall(account, muc_jid);
|
CallState? call_state = get_call_state_by_invite_id(account, from_jid, invite_id);
|
||||||
if (call_state == null) return;
|
if (call_state == null) return;
|
||||||
|
|
||||||
call_state.call.state = Call.State.OTHER_DEVICE;
|
call_state.call.state = Call.State.OTHER_DEVICE;
|
||||||
remove_call_from_datastructures(call_state.call);
|
remove_call_from_datastructures(call_state.call);
|
||||||
});
|
});
|
||||||
muji_meta_module.call_retracted.connect((from_jid, to_jid, muc_jid, message_type) => {
|
call_invites_module.call_retracted.connect((from_jid, to_jid, invite_id, message_type) => {
|
||||||
if (from_jid.equals_bare(account.bare_jid)) return;
|
if (from_jid.equals_bare(account.bare_jid)) return;
|
||||||
|
|
||||||
// The call was retracted by the counterpart
|
// The call was retracted by the counterpart
|
||||||
CallState? call_state = get_call_state_for_groupcall(account, muc_jid);
|
CallState? call_state = get_call_state_by_invite_id(account, from_jid, invite_id);
|
||||||
if (call_state == null) return;
|
if (call_state == null) return;
|
||||||
|
|
||||||
if (call_state.call.state != Call.State.RINGING) {
|
if (call_state.call.state != Call.State.RINGING) {
|
||||||
|
@ -411,7 +440,7 @@ namespace Dino {
|
||||||
call_state.call.state = Call.State.MISSED;
|
call_state.call.state = Call.State.MISSED;
|
||||||
remove_call_from_datastructures(call_state.call);
|
remove_call_from_datastructures(call_state.call);
|
||||||
});
|
});
|
||||||
muji_meta_module.call_rejected.connect((from_jid, to_jid, muc_jid, message_type) => {
|
call_invites_module.call_rejected.connect((from_jid, to_jid, muc_jid, message_type) => {
|
||||||
if (from_jid.equals_bare(account.bare_jid)) return;
|
if (from_jid.equals_bare(account.bare_jid)) return;
|
||||||
debug(@"[%s] %s rejected our MUJI invite to %s", account.bare_jid.to_string(), from_jid.to_string(), muc_jid.to_string());
|
debug(@"[%s] %s rejected our MUJI invite to %s", account.bare_jid.to_string(), from_jid.to_string(), muc_jid.to_string());
|
||||||
});
|
});
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class ModuleManager {
|
||||||
module_map[account].add(new Xep.JingleMessageInitiation.Module());
|
module_map[account].add(new Xep.JingleMessageInitiation.Module());
|
||||||
module_map[account].add(new Xep.JingleRawUdp.Module());
|
module_map[account].add(new Xep.JingleRawUdp.Module());
|
||||||
module_map[account].add(new Xep.Muji.Module());
|
module_map[account].add(new Xep.Muji.Module());
|
||||||
module_map[account].add(new Xep.MujiMeta.Module());
|
module_map[account].add(new Xep.CallInvites.Module());
|
||||||
module_map[account].add(new Xep.Coin.Module());
|
module_map[account].add(new Xep.Coin.Module());
|
||||||
initialize_account_modules(account, module_map[account]);
|
initialize_account_modules(account, module_map[account]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,7 +267,7 @@ public class Dino.Ui.FreeDesktopNotifier : NotificationProvider, Object {
|
||||||
if (content_notifications != null) {
|
if (content_notifications != null) {
|
||||||
foreach (uint32 id in content_notifications.values) {
|
foreach (uint32 id in content_notifications.values) {
|
||||||
try {
|
try {
|
||||||
yield dbus_notifications.close_notification(id);
|
dbus_notifications.close_notification.begin(id);
|
||||||
} catch (Error e) { }
|
} catch (Error e) { }
|
||||||
}
|
}
|
||||||
content_notifications.clear();
|
content_notifications.clear();
|
||||||
|
@ -277,7 +277,7 @@ public class Dino.Ui.FreeDesktopNotifier : NotificationProvider, Object {
|
||||||
public async void retract_conversation_notifications(Conversation conversation) {
|
public async void retract_conversation_notifications(Conversation conversation) {
|
||||||
if (content_notifications.has_key(conversation)) {
|
if (content_notifications.has_key(conversation)) {
|
||||||
try {
|
try {
|
||||||
yield dbus_notifications.close_notification(content_notifications[conversation]);
|
dbus_notifications.close_notification.begin(content_notifications[conversation]);
|
||||||
} catch (Error e) { }
|
} catch (Error e) { }
|
||||||
}
|
}
|
||||||
content_notifications.unset(conversation);
|
content_notifications.unset(conversation);
|
||||||
|
|
|
@ -132,12 +132,12 @@ SOURCES
|
||||||
"src/module/xep/0333_chat_markers.vala"
|
"src/module/xep/0333_chat_markers.vala"
|
||||||
"src/module/xep/0334_message_processing_hints.vala"
|
"src/module/xep/0334_message_processing_hints.vala"
|
||||||
"src/module/xep/0353_jingle_message_initiation.vala"
|
"src/module/xep/0353_jingle_message_initiation.vala"
|
||||||
|
"src/module/xep/0353_call_invite_message.vala"
|
||||||
"src/module/xep/0359_unique_stable_stanza_ids.vala"
|
"src/module/xep/0359_unique_stable_stanza_ids.vala"
|
||||||
"src/module/xep/0363_http_file_upload.vala"
|
"src/module/xep/0363_http_file_upload.vala"
|
||||||
"src/module/xep/0380_explicit_encryption.vala"
|
"src/module/xep/0380_explicit_encryption.vala"
|
||||||
"src/module/xep/0391_jingle_encrypted_transports.vala"
|
"src/module/xep/0391_jingle_encrypted_transports.vala"
|
||||||
"src/module/xep/0410_muc_self_ping.vala"
|
"src/module/xep/0410_muc_self_ping.vala"
|
||||||
"src/module/xep/muji_meta.vala"
|
|
||||||
"src/module/xep/pixbuf_storage.vala"
|
"src/module/xep/pixbuf_storage.vala"
|
||||||
|
|
||||||
"src/util.vala"
|
"src/util.vala"
|
||||||
|
|
|
@ -2,10 +2,10 @@ namespace Xmpp.Xep.MessageProcessingHints {
|
||||||
|
|
||||||
private const string NS_URI = "urn:xmpp:hints";
|
private const string NS_URI = "urn:xmpp:hints";
|
||||||
|
|
||||||
private const string HINT_NO_PERMANENT_STORE = "no-permanent-store";
|
public const string HINT_NO_PERMANENT_STORE = "no-permanent-store";
|
||||||
private const string HINT_NO_STORE = "no-store";
|
public const string HINT_NO_STORE = "no-store";
|
||||||
private const string HINT_NO_COPY = "no-copy";
|
public const string HINT_NO_COPY = "no-copy";
|
||||||
private const string HINT_STORE = "store";
|
public const string HINT_STORE = "store";
|
||||||
|
|
||||||
public static void set_message_hint(MessageStanza message, string message_hint) {
|
public static void set_message_hint(MessageStanza message, string message_hint) {
|
||||||
StanzaNode hint_node = (new StanzaNode.build(message_hint, NS_URI)).add_self_xmlns();
|
StanzaNode hint_node = (new StanzaNode.build(message_hint, NS_URI)).add_self_xmlns();
|
||||||
|
|
93
xmpp-vala/src/module/xep/0353_call_invite_message.vala
Normal file
93
xmpp-vala/src/module/xep/0353_call_invite_message.vala
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
using Gee;
|
||||||
|
namespace Xmpp.Xep.CallInvites {
|
||||||
|
|
||||||
|
public const string NS_URI = "urn:xmpp:call-invites:0";
|
||||||
|
|
||||||
|
public class Module : XmppStreamModule {
|
||||||
|
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "call_invites");
|
||||||
|
|
||||||
|
public signal void call_proposed(Jid from, Jid to, bool video, Gee.List<StanzaNode> join_methods, MessageStanza message);
|
||||||
|
public signal void call_retracted(Jid from, Jid to, string invite_id, string message_type);
|
||||||
|
public signal void call_accepted(Jid from, string invite_id, string message_type);
|
||||||
|
public signal void call_rejected(Jid from, Jid to, string invite_id, string message_type);
|
||||||
|
|
||||||
|
public void send_invite(XmppStream stream, Jid invitee, Jid muc_jid, bool video, string message_type) {
|
||||||
|
StanzaNode muji_node = new StanzaNode.build("muji", Muji.NS_URI).add_self_xmlns().put_attribute("room", muc_jid.to_string());
|
||||||
|
StanzaNode invite_node = new StanzaNode.build("propose", NS_URI).add_self_xmlns()
|
||||||
|
.put_attribute("video", video.to_string())
|
||||||
|
.put_node(muji_node);
|
||||||
|
MessageStanza invite_message = new MessageStanza() { to=invitee, type_=message_type };
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send_retract(XmppStream stream, Jid to, string invite_id, string message_type) {
|
||||||
|
send_message(stream, "retract", to, invite_id, message_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send_accept(XmppStream stream, Jid to, string invite_id, string message_type) {
|
||||||
|
send_message(stream, "accept", to, invite_id, message_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send_reject(XmppStream stream, Jid to, string invite_id, string message_type) {
|
||||||
|
send_message(stream, "reject", to, invite_id, message_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_message(XmppStream stream, string action, Jid to, string invite_id, string message_type) {
|
||||||
|
StanzaNode inner_node = new StanzaNode.build(action, NS_URI).add_self_xmlns().put_attribute("id", invite_id);
|
||||||
|
MessageStanza message = new MessageStanza() { to=to, type_=message_type };
|
||||||
|
message.stanza.put_node(inner_node);
|
||||||
|
MessageProcessingHints.set_message_hint(message, MessageProcessingHints.HINT_STORE);
|
||||||
|
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_received_message(XmppStream stream, MessageStanza message) {
|
||||||
|
Xep.MessageArchiveManagement.MessageFlag? mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message);
|
||||||
|
if (mam_flag != null) return;
|
||||||
|
|
||||||
|
StanzaNode? relevant_node = null;
|
||||||
|
|
||||||
|
foreach (StanzaNode node in message.stanza.sub_nodes) {
|
||||||
|
if (node.ns_uri == NS_URI) {
|
||||||
|
relevant_node = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (relevant_node == null) return;
|
||||||
|
|
||||||
|
if (relevant_node.name == "propose") {
|
||||||
|
if (relevant_node.sub_nodes.is_empty) return;
|
||||||
|
bool video = relevant_node.get_attribute_bool("video", false);
|
||||||
|
call_proposed(message.from, message.to, video, relevant_node.sub_nodes, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? invite_id = relevant_node.get_attribute("id");
|
||||||
|
if (invite_id == null) return;
|
||||||
|
|
||||||
|
switch (relevant_node.name) {
|
||||||
|
case "accept":
|
||||||
|
call_accepted(message.from, invite_id, message.type_);
|
||||||
|
break;
|
||||||
|
case "retract":
|
||||||
|
call_retracted(message.from, message.to, invite_id, message.type_);
|
||||||
|
break;
|
||||||
|
case "reject":
|
||||||
|
call_rejected(message.from, message.to, invite_id, message.type_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void attach(XmppStream stream) {
|
||||||
|
stream.get_module(MessageModule.IDENTITY).received_message.connect(on_received_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void detach(XmppStream stream) {
|
||||||
|
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string get_ns() { return NS_URI; }
|
||||||
|
public override string get_id() { return IDENTITY.id; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,117 +0,0 @@
|
||||||
using Gee;
|
|
||||||
namespace Xmpp.Xep.MujiMeta {
|
|
||||||
|
|
||||||
public const string NS_URI = "http://telepathy.freedesktop.org/muji";
|
|
||||||
|
|
||||||
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, 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, string message_type) {
|
|
||||||
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_=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, string message_type) {
|
|
||||||
send_jmi_message(stream, "retract", invitee, muc_jid, message_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send_invite_accept_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string message_type) {
|
|
||||||
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, MessageStanza.TYPE_CHAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send_invite_reject_to_peer(XmppStream stream, Jid invitor, Jid muc_jid, string message_type) {
|
|
||||||
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, MessageStanza.TYPE_CHAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void send_jmi_message(XmppStream stream, string name, Jid to, Jid muc, string message_type) {
|
|
||||||
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_= message_type ?? MessageStanza.TYPE_CHAT };
|
|
||||||
accepted_message.stanza.put_node(muji_node);
|
|
||||||
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, accepted_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void on_received_message(XmppStream stream, MessageStanza message) {
|
|
||||||
Xep.MessageArchiveManagement.MessageFlag? mam_flag = Xep.MessageArchiveManagement.MessageFlag.get_flag(message);
|
|
||||||
if (mam_flag != null) return;
|
|
||||||
|
|
||||||
var muji_node = message.stanza.get_subnode("muji", NS_URI);
|
|
||||||
if (muji_node == null) return;
|
|
||||||
|
|
||||||
StanzaNode? mi_node = null;
|
|
||||||
foreach (StanzaNode node in muji_node.sub_nodes) {
|
|
||||||
if (node.ns_uri == NS_URI) {
|
|
||||||
mi_node = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mi_node == null) return;
|
|
||||||
|
|
||||||
string? jid_str = mi_node.get_attribute("muc");
|
|
||||||
if (jid_str == null) return;
|
|
||||||
|
|
||||||
Jid muc_jid = null;
|
|
||||||
try {
|
|
||||||
muc_jid = new Jid(jid_str);
|
|
||||||
} catch (Error e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (mi_node.name) {
|
|
||||||
case "accept":
|
|
||||||
case "proceed":
|
|
||||||
call_accepted(message.from, muc_jid, message.type_);
|
|
||||||
break;
|
|
||||||
case "propose":
|
|
||||||
ArrayList<StanzaNode> descriptions = new ArrayList<StanzaNode>();
|
|
||||||
|
|
||||||
foreach (StanzaNode node in mi_node.sub_nodes) {
|
|
||||||
if (node.name != "description") continue;
|
|
||||||
descriptions.add(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptions.size > 0) {
|
|
||||||
call_proposed(message.from, message.to, muc_jid, descriptions, message.type_);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "retract":
|
|
||||||
call_retracted(message.from, message.to, muc_jid, message.type_);
|
|
||||||
break;
|
|
||||||
case "reject":
|
|
||||||
call_rejected(message.from, message.to, muc_jid, message.type_);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
|
||||||
stream.get_module(MessageModule.IDENTITY).received_message.connect(on_received_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void detach(XmppStream stream) {
|
|
||||||
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string get_ns() { return NS_URI; }
|
|
||||||
public override string get_id() { return IDENTITY.id; }
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue