MUC config form (data forms), MUC room info
This commit is contained in:
parent
142257a544
commit
3a8df2069e
|
@ -34,6 +34,7 @@ SOURCES
|
||||||
"src/module/tls.vala"
|
"src/module/tls.vala"
|
||||||
"src/module/util.vala"
|
"src/module/util.vala"
|
||||||
|
|
||||||
|
"src/module/xep/0004_data_forms.vala"
|
||||||
"src/module/xep/0030_service_discovery/flag.vala"
|
"src/module/xep/0030_service_discovery/flag.vala"
|
||||||
"src/module/xep/0030_service_discovery/identity.vala"
|
"src/module/xep/0030_service_discovery/identity.vala"
|
||||||
"src/module/xep/0030_service_discovery/info_result.vala"
|
"src/module/xep/0030_service_discovery/info_result.vala"
|
||||||
|
|
|
@ -45,8 +45,8 @@ public abstract class StanzaEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StanzaNode : StanzaEntry {
|
public class StanzaNode : StanzaEntry {
|
||||||
public ArrayList<StanzaNode> sub_nodes = new ArrayList<StanzaNode>();
|
public Gee.List<StanzaNode> sub_nodes = new ArrayList<StanzaNode>();
|
||||||
public ArrayList<StanzaAttribute> attributes = new ArrayList<StanzaAttribute>();
|
public Gee.List<StanzaAttribute> attributes = new ArrayList<StanzaAttribute>();
|
||||||
public bool has_nodes = false;
|
public bool has_nodes = false;
|
||||||
public bool pseudo = false;
|
public bool pseudo = false;
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ public class StanzaNode : StanzaEntry {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaAttribute> get_attributes_by_ns_uri(string ns_uri) {
|
public Gee.List<StanzaAttribute> get_attributes_by_ns_uri(string ns_uri) {
|
||||||
ArrayList<StanzaAttribute> ret = new ArrayList<StanzaAttribute> ();
|
ArrayList<StanzaAttribute> ret = new ArrayList<StanzaAttribute> ();
|
||||||
foreach (var attr in attributes) {
|
foreach (var attr in attributes) {
|
||||||
if (attr.ns_uri == ns_uri) ret.add(attr);
|
if (attr.ns_uri == ns_uri) ret.add(attr);
|
||||||
|
@ -181,7 +181,7 @@ public class StanzaNode : StanzaEntry {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaNode> get_subnodes(string name, string? ns_uri = null, bool recurse = false) {
|
public Gee.List<StanzaNode> get_subnodes(string name, string? ns_uri = null, bool recurse = false) {
|
||||||
ArrayList<StanzaNode> ret = new ArrayList<StanzaNode>();
|
ArrayList<StanzaNode> ret = new ArrayList<StanzaNode>();
|
||||||
string _name = name;
|
string _name = name;
|
||||||
string? _ns_uri = ns_uri;
|
string? _ns_uri = ns_uri;
|
||||||
|
@ -220,12 +220,12 @@ public class StanzaNode : StanzaEntry {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaNode> get_deep_subnodes(...) {
|
public Gee.List<StanzaNode> get_deep_subnodes(...) {
|
||||||
va_list l = va_list();
|
va_list l = va_list();
|
||||||
return get_deep_subnodes_(va_list.copy(l));
|
return get_deep_subnodes_(va_list.copy(l));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaNode> get_deep_subnodes_(va_list l) {
|
public Gee.List<StanzaNode> get_deep_subnodes_(va_list l) {
|
||||||
StanzaNode node = this;
|
StanzaNode node = this;
|
||||||
string? subnode_name = l.arg();
|
string? subnode_name = l.arg();
|
||||||
if (subnode_name == null) return new ArrayList<StanzaNode>();
|
if (subnode_name == null) return new ArrayList<StanzaNode>();
|
||||||
|
@ -240,11 +240,11 @@ public class StanzaNode : StanzaEntry {
|
||||||
return node.get_subnodes((!)subnode_name);
|
return node.get_subnodes((!)subnode_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaNode> get_all_subnodes() {
|
public Gee.List<StanzaNode> get_all_subnodes() {
|
||||||
return sub_nodes;
|
return sub_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<StanzaNode> get_deep_all_subnodes(...) {
|
public Gee.List<StanzaNode> get_deep_all_subnodes(...) {
|
||||||
va_list l = va_list();
|
va_list l = va_list();
|
||||||
StanzaNode? node = get_deep_subnode_(va_list.copy(l));
|
StanzaNode? node = get_deep_subnode_(va_list.copy(l));
|
||||||
if (node != null) return ((!)node).get_all_subnodes();
|
if (node != null) return ((!)node).get_all_subnodes();
|
||||||
|
|
|
@ -230,13 +230,13 @@ public class StanzaReader {
|
||||||
skip_single();
|
skip_single();
|
||||||
if (desc.contains(":")) {
|
if (desc.contains(":")) {
|
||||||
var split = desc.split(":");
|
var split = desc.split(":");
|
||||||
assert(split[0] == ns_state.find_name((!)res.ns_uri));
|
if (split[0] != ns_state.find_name((!)res.ns_uri)) throw new XmlError.BAD_XML("");
|
||||||
assert(split[1] == res.name);
|
if (split[1] != res.name) throw new XmlError.BAD_XML("");
|
||||||
} else {
|
} else {
|
||||||
assert(ns_state.current_ns_uri == res.ns_uri);
|
if (ns_state.current_ns_uri != res.ns_uri) throw new XmlError.BAD_XML("");
|
||||||
assert(desc == res.name);
|
if (desc != res.name) throw new XmlError.BAD_XML("");
|
||||||
}
|
}
|
||||||
return res;
|
finishNodeSeen = true;
|
||||||
} else {
|
} else {
|
||||||
res.sub_nodes.add(read_stanza_node(ns_state.clone()));
|
res.sub_nodes.add(read_stanza_node(ns_state.clone()));
|
||||||
ns_state = baseNs ?? new NamespaceState.for_stanza();
|
ns_state = baseNs ?? new NamespaceState.for_stanza();
|
||||||
|
@ -245,6 +245,7 @@ public class StanzaReader {
|
||||||
res.sub_nodes.add(read_text_node());
|
res.sub_nodes.add(read_text_node());
|
||||||
}
|
}
|
||||||
} while (!finishNodeSeen);
|
} while (!finishNodeSeen);
|
||||||
|
if (res.sub_nodes.size == 0) res.has_nodes = false;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,15 @@ namespace Xmpp.Iq {
|
||||||
private HashMap<string, ResponseListener> responseListeners = new HashMap<string, ResponseListener>();
|
private HashMap<string, ResponseListener> responseListeners = new HashMap<string, ResponseListener>();
|
||||||
private HashMap<string, ArrayList<Handler>> namespaceRegistrants = new HashMap<string, ArrayList<Handler>>();
|
private HashMap<string, ArrayList<Handler>> namespaceRegistrants = new HashMap<string, ArrayList<Handler>>();
|
||||||
|
|
||||||
[CCode (has_target = false)] public delegate void OnResult(XmppStream stream, Iq.Stanza iq, Object reference);
|
[CCode (has_target = false)] public delegate void OnResult(XmppStream stream, Iq.Stanza iq, Object store);
|
||||||
public void send_iq(XmppStream stream, Iq.Stanza iq, OnResult? listener = null, Object? reference = null) {
|
public void send_iq(XmppStream stream, Iq.Stanza iq, OnResult? listener = null, Object? store = null) {
|
||||||
try {
|
try {
|
||||||
stream.write(iq.stanza);
|
stream.write(iq.stanza);
|
||||||
} catch (IOStreamError e) {
|
} catch (IOStreamError e) {
|
||||||
print(@"$(e.message)\n");
|
print(@"$(e.message)\n");
|
||||||
}
|
}
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
responseListeners[iq.id] = new ResponseListener(listener, reference);
|
responseListeners[iq.id] = new ResponseListener(listener, store);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,9 @@ namespace Xmpp.Iq {
|
||||||
responseListeners.unset(iq.id);
|
responseListeners.unset(iq.id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ArrayList<StanzaNode> children = node.get_all_subnodes();
|
Gee.List<StanzaNode> children = node.get_all_subnodes();
|
||||||
if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) {
|
if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) {
|
||||||
ArrayList<Handler> handlers = namespaceRegistrants[children[0].ns_uri];
|
Gee.List<Handler> handlers = namespaceRegistrants[children[0].ns_uri];
|
||||||
foreach (Handler handler in handlers) {
|
foreach (Handler handler in handlers) {
|
||||||
if (iq.type_ == Iq.Stanza.TYPE_GET) {
|
if (iq.type_ == Iq.Stanza.TYPE_GET) {
|
||||||
handler.on_iq_get(stream, iq);
|
handler.on_iq_get(stream, iq);
|
||||||
|
|
|
@ -2,7 +2,7 @@ using Xmpp.Core;
|
||||||
|
|
||||||
namespace Xmpp {
|
namespace Xmpp {
|
||||||
|
|
||||||
public class Stanza {
|
public class Stanza : Object {
|
||||||
|
|
||||||
public const string ATTRIBUTE_FROM = "from";
|
public const string ATTRIBUTE_FROM = "from";
|
||||||
public const string ATTRIBUTE_ID = "id";
|
public const string ATTRIBUTE_ID = "id";
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace Xmpp {
|
||||||
|
|
||||||
public string condition {
|
public string condition {
|
||||||
get {
|
get {
|
||||||
ArrayList<StanzaNode> subnodes = error_node.sub_nodes;
|
Gee.List<StanzaNode> subnodes = error_node.sub_nodes;
|
||||||
foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns
|
foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns
|
||||||
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-stanzas") {
|
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-stanzas") {
|
||||||
return subnode.name;
|
return subnode.name;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace Xmpp.StreamError {
|
||||||
|
|
||||||
private Flag generate_error_flag(StanzaNode node) {
|
private Flag generate_error_flag(StanzaNode node) {
|
||||||
string? subnode_name = null;
|
string? subnode_name = null;
|
||||||
ArrayList<StanzaNode> subnodes = node.sub_nodes;
|
Gee.List<StanzaNode> subnodes = node.sub_nodes;
|
||||||
foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns
|
foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns
|
||||||
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") {
|
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") {
|
||||||
subnode_name = subnode.name;
|
subnode_name = subnode.name;
|
||||||
|
|
208
xmpp-vala/src/module/xep/0004_data_forms.vala
Normal file
208
xmpp-vala/src/module/xep/0004_data_forms.vala
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
using Gee;
|
||||||
|
|
||||||
|
using Xmpp.Core;
|
||||||
|
|
||||||
|
namespace Xmpp.Xep.DataForms {
|
||||||
|
|
||||||
|
public const string NS_URI = "jabber:x:data";
|
||||||
|
|
||||||
|
public class DataForm {
|
||||||
|
|
||||||
|
public StanzaNode stanza_node { get; set; }
|
||||||
|
public Gee.List<Field> fields = new ArrayList<Field>();
|
||||||
|
|
||||||
|
public XmppStream stream;
|
||||||
|
public OnResult on_result;
|
||||||
|
public Object? store;
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
StanzaNode stanza_node = new StanzaNode.build("x", NS_URI);
|
||||||
|
stanza_node.add_self_xmlns().set_attribute("type", "cancel");
|
||||||
|
on_result(stream, stanza_node, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submit() {
|
||||||
|
stanza_node.set_attribute("type", "submit");
|
||||||
|
on_result(stream, stanza_node, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
BOOLEAN,
|
||||||
|
FIXED,
|
||||||
|
HIDDEN,
|
||||||
|
JID_MULTI,
|
||||||
|
LIST_SINGLE,
|
||||||
|
LIST_MULTI,
|
||||||
|
TEXT_PRIVATE,
|
||||||
|
TEXT_SINGLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Option {
|
||||||
|
public string label { get; set; }
|
||||||
|
public string value { get; set; }
|
||||||
|
|
||||||
|
public Option(string label, string value) {
|
||||||
|
this.label = label;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Field {
|
||||||
|
public string label {
|
||||||
|
get { return node.get_attribute("label", NS_URI); }
|
||||||
|
set { node.set_attribute("label", value); }
|
||||||
|
}
|
||||||
|
public StanzaNode node { get; set; }
|
||||||
|
public abstract Type type_ { get; internal set; }
|
||||||
|
public string var {
|
||||||
|
get { return node.get_attribute("var", NS_URI); }
|
||||||
|
set { node.set_attribute("var", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field(StanzaNode node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Gee.List<string> get_values() {
|
||||||
|
Gee.List<string> ret = new ArrayList<string>();
|
||||||
|
Gee.List<StanzaNode> value_nodes = node.get_subnodes("value", NS_URI);
|
||||||
|
foreach (StanzaNode node in value_nodes) {
|
||||||
|
ret.add(node.get_string_content());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string get_value_string() {
|
||||||
|
Gee.List<string> values = get_values();
|
||||||
|
return values.size > 0 ? values[0] : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void set_value_string(string val) {
|
||||||
|
StanzaNode? value_node = node.get_subnode("value", NS_URI);
|
||||||
|
if (value_node == null) {
|
||||||
|
value_node = new StanzaNode.build("value", NS_URI);
|
||||||
|
node.put_node(value_node);
|
||||||
|
}
|
||||||
|
value_node.sub_nodes.clear();
|
||||||
|
value_node.put_node(new StanzaNode.text(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void add_value_string(string val) {
|
||||||
|
StanzaNode node = new StanzaNode.build("value");
|
||||||
|
node.put_node(new StanzaNode.text(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Gee.List<Option>? get_options() {
|
||||||
|
Gee.List<Option> ret = new ArrayList<Option>();
|
||||||
|
Gee.List<StanzaNode> option_nodes = node.get_subnodes("option", NS_URI);
|
||||||
|
foreach (StanzaNode node in option_nodes) {
|
||||||
|
Option option = new Option(node.get_attribute("label", NS_URI), node.get_subnode("value").get_string_content());
|
||||||
|
ret.add(option);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BooleanField : Field {
|
||||||
|
public override Type type_ { get; internal set; default=Type.BOOLEAN; }
|
||||||
|
public bool value {
|
||||||
|
get { return get_value_string() == "1"; }
|
||||||
|
set { set_value_string(value ? "1" : "0"); }
|
||||||
|
}
|
||||||
|
public BooleanField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FixedField : Field {
|
||||||
|
public override Type type_ { get; internal set; default=Type.FIXED; }
|
||||||
|
public string value {
|
||||||
|
owned get { return get_value_string(); }
|
||||||
|
set { set_value_string(value); }
|
||||||
|
}
|
||||||
|
public FixedField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HiddenField : Field {
|
||||||
|
public override Type type_ { get; internal set; default=Type.HIDDEN; }
|
||||||
|
public HiddenField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JidMultiField : Field {
|
||||||
|
public Gee.List<Option> options { owned get { return get_options(); } }
|
||||||
|
public override Type type_ { get; internal set; default=Type.JID_MULTI; }
|
||||||
|
public Gee.List<string> value { get; set; }
|
||||||
|
public JidMultiField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ListSingleField : Field {
|
||||||
|
public Gee.List<Option> options { owned get { return get_options(); } }
|
||||||
|
public override Type type_ { get; internal set; default=Type.LIST_SINGLE; }
|
||||||
|
public string value {
|
||||||
|
owned get { return get_value_string(); }
|
||||||
|
set { set_value_string(value); }
|
||||||
|
}
|
||||||
|
public ListSingleField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ListMultiField : Field {
|
||||||
|
public Gee.List<Option> options { owned get { return get_options(); } }
|
||||||
|
public override Type type_ { get; internal set; default=Type.LIST_MULTI; }
|
||||||
|
public Gee.List<string> value { get; set; }
|
||||||
|
public ListMultiField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextPrivateField : Field {
|
||||||
|
public override Type type_ { get; internal set; default=Type.TEXT_PRIVATE; }
|
||||||
|
public string value {
|
||||||
|
owned get { return get_value_string(); }
|
||||||
|
set { set_value_string(value); }
|
||||||
|
}
|
||||||
|
public TextPrivateField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextSingleField : Field {
|
||||||
|
public override Type type_ { get; internal set; default=Type.TEXT_SINGLE; }
|
||||||
|
public string value {
|
||||||
|
owned get { return get_value_string(); }
|
||||||
|
set { set_value_string(value); }
|
||||||
|
}
|
||||||
|
public TextSingleField(StanzaNode node) { base(node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO text-multi
|
||||||
|
|
||||||
|
internal DataForm(StanzaNode node, XmppStream stream, OnResult on_result, Object? store) {
|
||||||
|
this.stanza_node = node;
|
||||||
|
this.stream = stream;
|
||||||
|
this.on_result = on_result;
|
||||||
|
this.store = store;
|
||||||
|
Gee.List<StanzaNode> field_nodes = node.get_subnodes("field", NS_URI);
|
||||||
|
foreach (StanzaNode field_node in field_nodes) {
|
||||||
|
string? type = field_node.get_attribute("type", NS_URI);
|
||||||
|
switch (type) {
|
||||||
|
case "boolean":
|
||||||
|
fields.add(new BooleanField(field_node)); break;
|
||||||
|
case "fixed":
|
||||||
|
fields.add(new FixedField(field_node)); break;
|
||||||
|
case "hidden":
|
||||||
|
fields.add(new HiddenField(field_node)); break;
|
||||||
|
case "jid-multi":
|
||||||
|
fields.add(new JidMultiField(field_node)); break;
|
||||||
|
case "list-single":
|
||||||
|
fields.add(new ListSingleField(field_node)); break;
|
||||||
|
case "list-multi":
|
||||||
|
fields.add(new ListMultiField(field_node)); break;
|
||||||
|
case "text-private":
|
||||||
|
fields.add(new TextPrivateField(field_node)); break;
|
||||||
|
case "text-single":
|
||||||
|
fields.add(new TextSingleField(field_node)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (has_target = false)] public delegate void OnResult(XmppStream stream, StanzaNode node, Object? store);
|
||||||
|
public static DataForm? create(XmppStream stream, StanzaNode node, OnResult on_result, Object? store) {
|
||||||
|
return new DataForm(node, stream, on_result, store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ namespace Xmpp.Xep.Muc {
|
||||||
public class Flag : XmppStreamFlag {
|
public class Flag : XmppStreamFlag {
|
||||||
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "muc");
|
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "muc");
|
||||||
|
|
||||||
|
private HashMap<string, Gee.List<Feature>> room_features = new HashMap<string, Gee.List<Feature>>();
|
||||||
|
|
||||||
private HashMap<string, string> enter_ids = new HashMap<string, string>();
|
private HashMap<string, string> enter_ids = new HashMap<string, string>();
|
||||||
private HashMap<string, string> own_nicks = new HashMap<string, string>();
|
private HashMap<string, string> own_nicks = new HashMap<string, string>();
|
||||||
private HashMap<string, string> subjects = new HashMap<string, string>();
|
private HashMap<string, string> subjects = new HashMap<string, string>();
|
||||||
|
@ -16,6 +18,10 @@ public class Flag : XmppStreamFlag {
|
||||||
private HashMap<string, HashMap<string, Affiliation>> affiliations = new HashMap<string, HashMap<string, Affiliation>>();
|
private HashMap<string, HashMap<string, Affiliation>> affiliations = new HashMap<string, HashMap<string, Affiliation>>();
|
||||||
private HashMap<string, Role> occupant_role = new HashMap<string, Role>();
|
private HashMap<string, Role> occupant_role = new HashMap<string, Role>();
|
||||||
|
|
||||||
|
public bool has_room_feature(string jid, Feature feature) {
|
||||||
|
return room_features.has_key(jid) && room_features[jid].contains(feature);
|
||||||
|
}
|
||||||
|
|
||||||
public string? get_real_jid(string full_jid) { return occupant_real_jids[full_jid]; }
|
public string? get_real_jid(string full_jid) { return occupant_real_jids[full_jid]; }
|
||||||
|
|
||||||
public Gee.List<string> get_offline_members(string full_jid) {
|
public Gee.List<string> get_offline_members(string full_jid) {
|
||||||
|
@ -53,6 +59,10 @@ public class Flag : XmppStreamFlag {
|
||||||
|
|
||||||
public string? get_muc_subject(string bare_jid) { return subjects[bare_jid]; }
|
public string? get_muc_subject(string bare_jid) { return subjects[bare_jid]; }
|
||||||
|
|
||||||
|
internal void set_room_features(string jid, Gee.List<Feature> features) {
|
||||||
|
room_features[jid] = features;
|
||||||
|
}
|
||||||
|
|
||||||
internal void set_real_jid(string full_jid, string real_jid) { occupant_real_jids[full_jid] = real_jid; }
|
internal void set_real_jid(string full_jid, string real_jid) { occupant_real_jids[full_jid] = real_jid; }
|
||||||
|
|
||||||
internal void set_offline_member(string muc_jid, string real_jid, Affiliation affiliation) {
|
internal void set_offline_member(string muc_jid, string real_jid, Affiliation affiliation) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace Xmpp.Xep.Muc {
|
||||||
|
|
||||||
private const string NS_URI = "http://jabber.org/protocol/muc";
|
private const string NS_URI = "http://jabber.org/protocol/muc";
|
||||||
private const string NS_URI_ADMIN = NS_URI + "#admin";
|
private const string NS_URI_ADMIN = NS_URI + "#admin";
|
||||||
|
private const string NS_URI_OWNER = NS_URI + "#owner";
|
||||||
private const string NS_URI_USER = NS_URI + "#user";
|
private const string NS_URI_USER = NS_URI + "#user";
|
||||||
|
|
||||||
public enum MucEnterError {
|
public enum MucEnterError {
|
||||||
|
@ -32,6 +33,25 @@ public enum Role {
|
||||||
VISITOR
|
VISITOR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Feature {
|
||||||
|
REGISTER,
|
||||||
|
ROOMCONFIG,
|
||||||
|
ROOMINFO,
|
||||||
|
HIDDEN,
|
||||||
|
MEMBERS_ONLY,
|
||||||
|
MODERATED,
|
||||||
|
NON_ANONYMOUS,
|
||||||
|
OPEN,
|
||||||
|
PASSWORD_PROTECTED,
|
||||||
|
PERSISTENT,
|
||||||
|
PUBLIC,
|
||||||
|
ROOMS,
|
||||||
|
SEMI_ANONYMOUS,
|
||||||
|
TEMPORARY,
|
||||||
|
UNMODERATED,
|
||||||
|
UNSECURED
|
||||||
|
}
|
||||||
|
|
||||||
public class Module : XmppStreamModule {
|
public class Module : XmppStreamModule {
|
||||||
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0045_muc_module");
|
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0045_muc_module");
|
||||||
|
|
||||||
|
@ -56,6 +76,8 @@ public class Module : XmppStreamModule {
|
||||||
presence.stanza.put_node(x_node);
|
presence.stanza.put_node(x_node);
|
||||||
|
|
||||||
stream.get_flag(Flag.IDENTITY).start_muc_enter(bare_jid, presence.id);
|
stream.get_flag(Flag.IDENTITY).start_muc_enter(bare_jid, presence.id);
|
||||||
|
|
||||||
|
query_room_info(stream, bare_jid);
|
||||||
stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence);
|
stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +107,19 @@ public class Module : XmppStreamModule {
|
||||||
change_role(stream, jid, nick, "none");
|
change_role(stream, jid, nick, "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CCode (has_target = false)] public delegate void OnConfigFormResult(XmppStream stream, string jid, DataForms.DataForm data_form, Object? store);
|
||||||
|
public void get_config_form(XmppStream stream, string jid, OnConfigFormResult listener, Object? store) {
|
||||||
|
Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_OWNER).add_self_xmlns()) { to=jid };
|
||||||
|
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq, store) => {
|
||||||
|
Tuple<OnConfigFormResult, Object?> tuple = store as Tuple<OnConfigFormResult, Object?>;
|
||||||
|
StanzaNode? x_node = iq.stanza.get_deep_subnode(NS_URI_OWNER + ":query", DataForms.NS_URI + ":x");
|
||||||
|
if (x_node != null) {
|
||||||
|
DataForms.DataForm data_form = DataForms.DataForm.create(stream, x_node, on_config_form_result, iq);
|
||||||
|
tuple.a(stream, iq.from, data_form, tuple.b);
|
||||||
|
}
|
||||||
|
}, Tuple.create(listener, store));
|
||||||
|
}
|
||||||
|
|
||||||
public override void attach(XmppStream stream) {
|
public override void attach(XmppStream stream) {
|
||||||
stream.add_flag(new Muc.Flag());
|
stream.add_flag(new Muc.Flag());
|
||||||
Message.Module.require(stream);
|
Message.Module.require(stream);
|
||||||
|
@ -225,8 +260,37 @@ public class Module : XmppStreamModule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CCode (has_target = false)] public delegate void OnResult(XmppStream stream, Gee.List<string> jids, Object? store);
|
private void query_room_info(XmppStream stream, string jid) {
|
||||||
private void query_affiliation(XmppStream stream, string jid, string affiliation, OnResult? on_result, Object? store) {
|
stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid, (stream, query_result, store) => {
|
||||||
|
Gee.List<Feature> features = new ArrayList<Feature>();
|
||||||
|
foreach (string feature in query_result.features) {
|
||||||
|
Feature? parsed = null;
|
||||||
|
switch (feature) {
|
||||||
|
case "http://jabber.org/protocol/muc#register": parsed = Feature.REGISTER; break;
|
||||||
|
case "http://jabber.org/protocol/muc#roomconfig": parsed = Feature.ROOMCONFIG; break;
|
||||||
|
case "http://jabber.org/protocol/muc#roominfo": parsed = Feature.ROOMINFO; break;
|
||||||
|
case "muc_hidden": parsed = Feature.HIDDEN; break;
|
||||||
|
case "muc_membersonly": parsed = Feature.MEMBERS_ONLY; break;
|
||||||
|
case "muc_moderated": parsed = Feature.MODERATED; break;
|
||||||
|
case "muc_nonanonymous": parsed = Feature.NON_ANONYMOUS; break;
|
||||||
|
case "muc_open": parsed = Feature.OPEN; break;
|
||||||
|
case "muc_passwordprotected": parsed = Feature.PASSWORD_PROTECTED; break;
|
||||||
|
case "muc_persistent": parsed = Feature.PERSISTENT; break;
|
||||||
|
case "muc_public": parsed = Feature.PUBLIC; break;
|
||||||
|
case "muc_rooms": parsed = Feature.ROOMS; break;
|
||||||
|
case "muc_semianonymous": parsed = Feature.SEMI_ANONYMOUS; break;
|
||||||
|
case "muc_temporary": parsed = Feature.TEMPORARY; break;
|
||||||
|
case "muc_unmoderated": parsed = Feature.UNMODERATED; break;
|
||||||
|
case "muc_unsecured": parsed = Feature.UNSECURED; break;
|
||||||
|
}
|
||||||
|
if (parsed != null) features.add(parsed);
|
||||||
|
}
|
||||||
|
stream.get_flag(Flag.IDENTITY).set_room_features(query_result.iq.from, features);
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (has_target = false)] public delegate void OnAffiliationResult(XmppStream stream, Gee.List<string> jids, Object? store);
|
||||||
|
private void query_affiliation(XmppStream stream, string jid, string affiliation, OnAffiliationResult? on_result, Object? store) {
|
||||||
Iq.Stanza iq = new Iq.Stanza.get(
|
Iq.Stanza iq = new Iq.Stanza.get(
|
||||||
new StanzaNode.build("query", NS_URI_ADMIN)
|
new StanzaNode.build("query", NS_URI_ADMIN)
|
||||||
.add_self_xmlns()
|
.add_self_xmlns()
|
||||||
|
@ -234,7 +298,7 @@ public class Module : XmppStreamModule {
|
||||||
.put_attribute("affiliation", affiliation))
|
.put_attribute("affiliation", affiliation))
|
||||||
) { to=jid };
|
) { to=jid };
|
||||||
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq, store) => {
|
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, (stream, iq, store) => {
|
||||||
Tuple<OnResult?, Object?> tuple = store as Tuple<OnResult?, Object?>;
|
Tuple<OnAffiliationResult?, Object?> tuple = store as Tuple<OnAffiliationResult?, Object?>;
|
||||||
if (iq.is_error()) return;
|
if (iq.is_error()) return;
|
||||||
StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_ADMIN);
|
StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_ADMIN);
|
||||||
if (query_node == null) return;
|
if (query_node == null) return;
|
||||||
|
@ -252,6 +316,15 @@ public class Module : XmppStreamModule {
|
||||||
}, Tuple.create(on_result, store));
|
}, Tuple.create(on_result, store));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void on_config_form_result(XmppStream stream, StanzaNode node, Object? store) {
|
||||||
|
Iq.Stanza form_iq = store as Iq.Stanza;
|
||||||
|
StanzaNode stanza_node = new StanzaNode.build("query", NS_URI_OWNER);
|
||||||
|
stanza_node.add_self_xmlns().put_node(node);
|
||||||
|
Iq.Stanza set_iq = new Iq.Stanza.set(stanza_node);
|
||||||
|
set_iq.to = form_iq.from;
|
||||||
|
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, set_iq);
|
||||||
|
}
|
||||||
|
|
||||||
private static ArrayList<int> get_status_codes(StanzaNode x_node) {
|
private static ArrayList<int> get_status_codes(StanzaNode x_node) {
|
||||||
ArrayList<int> ret = new ArrayList<int>();
|
ArrayList<int> ret = new ArrayList<int>();
|
||||||
foreach (StanzaNode status_node in x_node.get_subnodes("status", NS_URI_USER)) {
|
foreach (StanzaNode status_node in x_node.get_subnodes("status", NS_URI_USER)) {
|
||||||
|
|
|
@ -85,9 +85,9 @@ public class Module : XmppStreamModule {
|
||||||
public override string get_ns() { return NS_URI; }
|
public override string get_ns() { return NS_URI; }
|
||||||
public override string get_id() { return IDENTITY.id; }
|
public override string get_id() { return IDENTITY.id; }
|
||||||
|
|
||||||
private static ArrayList<Conference> get_conferences_from_stanza(StanzaNode node) {
|
private static Gee.List<Conference> get_conferences_from_stanza(StanzaNode node) {
|
||||||
ArrayList<Conference> conferences = new ArrayList<Conference>();
|
Gee.List<Conference> conferences = new ArrayList<Conference>();
|
||||||
ArrayList<StanzaNode> conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI);
|
Gee.List<StanzaNode> conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI);
|
||||||
foreach (StanzaNode conferenceNode in conferenceNodes) {
|
foreach (StanzaNode conferenceNode in conferenceNodes) {
|
||||||
Conference? conference = Conference.create_from_stanza_node(conferenceNode);
|
Conference? conference = Conference.create_from_stanza_node(conferenceNode);
|
||||||
conferences.add(conference);
|
conferences.add(conference);
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class Module : XmppStreamModule {
|
||||||
|
|
||||||
private void on_received_message(XmppStream stream, Message.Stanza message) {
|
private void on_received_message(XmppStream stream, Message.Stanza message) {
|
||||||
if (!message.is_error()) {
|
if (!message.is_error()) {
|
||||||
ArrayList<StanzaNode> nodes = message.stanza.get_all_subnodes();
|
Gee.List<StanzaNode> nodes = message.stanza.get_all_subnodes();
|
||||||
foreach (StanzaNode node in nodes) {
|
foreach (StanzaNode node in nodes) {
|
||||||
if (node.ns_uri == NS_URI &&
|
if (node.ns_uri == NS_URI &&
|
||||||
node.name in STATES) {
|
node.name in STATES) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class Module : XmppStreamModule {
|
||||||
send_marker(stream, message.from, message.id, message.type_, MARKER_RECEIVED);
|
send_marker(stream, message.from, message.id, message.type_, MARKER_RECEIVED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ArrayList<StanzaNode> nodes = message.stanza.get_all_subnodes();
|
Gee.List<StanzaNode> nodes = message.stanza.get_all_subnodes();
|
||||||
foreach (StanzaNode node in nodes) {
|
foreach (StanzaNode node in nodes) {
|
||||||
if (node.ns_uri == NS_URI && node.name in MARKERS) {
|
if (node.ns_uri == NS_URI && node.name in MARKERS) {
|
||||||
marker_received(stream, message.from, node.name, node.get_attribute("id", NS_URI));
|
marker_received(stream, message.from, node.name, node.get_attribute("id", NS_URI));
|
||||||
|
|
Loading…
Reference in a new issue