aboutsummaryrefslogtreecommitdiff
path: root/xmpp-vala/src/module
diff options
context:
space:
mode:
Diffstat (limited to 'xmpp-vala/src/module')
-rw-r--r--xmpp-vala/src/module/iq/module.vala10
-rw-r--r--xmpp-vala/src/module/stanza.vala2
-rw-r--r--xmpp-vala/src/module/stanza_error.vala2
-rw-r--r--xmpp-vala/src/module/stream_error.vala2
-rw-r--r--xmpp-vala/src/module/xep/0004_data_forms.vala208
-rw-r--r--xmpp-vala/src/module/xep/0045_muc/flag.vala10
-rw-r--r--xmpp-vala/src/module/xep/0045_muc/module.vala79
-rw-r--r--xmpp-vala/src/module/xep/0048_bookmarks/module.vala6
-rw-r--r--xmpp-vala/src/module/xep/0085_chat_state_notifications.vala2
-rw-r--r--xmpp-vala/src/module/xep/0333_chat_markers.vala2
10 files changed, 307 insertions, 16 deletions
diff --git a/xmpp-vala/src/module/iq/module.vala b/xmpp-vala/src/module/iq/module.vala
index eed3389d..909ec984 100644
--- a/xmpp-vala/src/module/iq/module.vala
+++ b/xmpp-vala/src/module/iq/module.vala
@@ -11,15 +11,15 @@ namespace Xmpp.Iq {
private HashMap<string, ResponseListener> responseListeners = new HashMap<string, ResponseListener>();
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);
- public void send_iq(XmppStream stream, Iq.Stanza iq, OnResult? listener = null, Object? reference = null) {
+ [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? store = null) {
try {
stream.write(iq.stanza);
} catch (IOStreamError e) {
print(@"$(e.message)\n");
}
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);
}
} 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)) {
- ArrayList<Handler> handlers = namespaceRegistrants[children[0].ns_uri];
+ Gee.List<Handler> handlers = namespaceRegistrants[children[0].ns_uri];
foreach (Handler handler in handlers) {
if (iq.type_ == Iq.Stanza.TYPE_GET) {
handler.on_iq_get(stream, iq);
diff --git a/xmpp-vala/src/module/stanza.vala b/xmpp-vala/src/module/stanza.vala
index 85cbadb7..2bf3e29b 100644
--- a/xmpp-vala/src/module/stanza.vala
+++ b/xmpp-vala/src/module/stanza.vala
@@ -2,7 +2,7 @@ using Xmpp.Core;
namespace Xmpp {
- public class Stanza {
+ public class Stanza : Object {
public const string ATTRIBUTE_FROM = "from";
public const string ATTRIBUTE_ID = "id";
diff --git a/xmpp-vala/src/module/stanza_error.vala b/xmpp-vala/src/module/stanza_error.vala
index be4633e9..b34caeb0 100644
--- a/xmpp-vala/src/module/stanza_error.vala
+++ b/xmpp-vala/src/module/stanza_error.vala
@@ -40,7 +40,7 @@ namespace Xmpp {
public string condition {
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
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-stanzas") {
return subnode.name;
diff --git a/xmpp-vala/src/module/stream_error.vala b/xmpp-vala/src/module/stream_error.vala
index bd292d2b..fa54d06f 100644
--- a/xmpp-vala/src/module/stream_error.vala
+++ b/xmpp-vala/src/module/stream_error.vala
@@ -32,7 +32,7 @@ namespace Xmpp.StreamError {
private Flag generate_error_flag(StanzaNode node) {
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
if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") {
subnode_name = subnode.name;
diff --git a/xmpp-vala/src/module/xep/0004_data_forms.vala b/xmpp-vala/src/module/xep/0004_data_forms.vala
new file mode 100644
index 00000000..add2fa9a
--- /dev/null
+++ b/xmpp-vala/src/module/xep/0004_data_forms.vala
@@ -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);
+ }
+}
+
+} \ No newline at end of file
diff --git a/xmpp-vala/src/module/xep/0045_muc/flag.vala b/xmpp-vala/src/module/xep/0045_muc/flag.vala
index 159f0193..cf729bf9 100644
--- a/xmpp-vala/src/module/xep/0045_muc/flag.vala
+++ b/xmpp-vala/src/module/xep/0045_muc/flag.vala
@@ -7,6 +7,8 @@ namespace Xmpp.Xep.Muc {
public class Flag : XmppStreamFlag {
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> own_nicks = 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, 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 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]; }
+ 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_offline_member(string muc_jid, string real_jid, Affiliation affiliation) {
diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala
index 9893f8a3..c098fd14 100644
--- a/xmpp-vala/src/module/xep/0045_muc/module.vala
+++ b/xmpp-vala/src/module/xep/0045_muc/module.vala
@@ -6,6 +6,7 @@ namespace Xmpp.Xep.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_OWNER = NS_URI + "#owner";
private const string NS_URI_USER = NS_URI + "#user";
public enum MucEnterError {
@@ -32,6 +33,25 @@ public enum Role {
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 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);
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);
}
@@ -85,6 +107,19 @@ public class Module : XmppStreamModule {
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) {
stream.add_flag(new Muc.Flag());
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_affiliation(XmppStream stream, string jid, string affiliation, OnResult? on_result, Object? store) {
+ private void query_room_info(XmppStream stream, string jid) {
+ 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(
new StanzaNode.build("query", NS_URI_ADMIN)
.add_self_xmlns()
@@ -234,7 +298,7 @@ public class Module : XmppStreamModule {
.put_attribute("affiliation", affiliation))
) { to=jid };
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;
StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_ADMIN);
if (query_node == null) return;
@@ -252,6 +316,15 @@ public class Module : XmppStreamModule {
}, 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) {
ArrayList<int> ret = new ArrayList<int>();
foreach (StanzaNode status_node in x_node.get_subnodes("status", NS_URI_USER)) {
diff --git a/xmpp-vala/src/module/xep/0048_bookmarks/module.vala b/xmpp-vala/src/module/xep/0048_bookmarks/module.vala
index bf5bd612..f2030ff6 100644
--- a/xmpp-vala/src/module/xep/0048_bookmarks/module.vala
+++ b/xmpp-vala/src/module/xep/0048_bookmarks/module.vala
@@ -85,9 +85,9 @@ public class Module : XmppStreamModule {
public override string get_ns() { return NS_URI; }
public override string get_id() { return IDENTITY.id; }
- private static ArrayList<Conference> get_conferences_from_stanza(StanzaNode node) {
- ArrayList<Conference> conferences = new ArrayList<Conference>();
- ArrayList<StanzaNode> conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI);
+ private static Gee.List<Conference> get_conferences_from_stanza(StanzaNode node) {
+ Gee.List<Conference> conferences = new ArrayList<Conference>();
+ Gee.List<StanzaNode> conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI);
foreach (StanzaNode conferenceNode in conferenceNodes) {
Conference? conference = Conference.create_from_stanza_node(conferenceNode);
conferences.add(conference);
diff --git a/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala b/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala
index 6212cade..9b5ba075 100644
--- a/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala
+++ b/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala
@@ -56,7 +56,7 @@ public class Module : XmppStreamModule {
private void on_received_message(XmppStream stream, Message.Stanza message) {
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) {
if (node.ns_uri == NS_URI &&
node.name in STATES) {
diff --git a/xmpp-vala/src/module/xep/0333_chat_markers.vala b/xmpp-vala/src/module/xep/0333_chat_markers.vala
index 7ac70806..e6bab2c6 100644
--- a/xmpp-vala/src/module/xep/0333_chat_markers.vala
+++ b/xmpp-vala/src/module/xep/0333_chat_markers.vala
@@ -57,7 +57,7 @@ public class Module : XmppStreamModule {
send_marker(stream, message.from, message.id, message.type_, MARKER_RECEIVED);
return;
}
- ArrayList<StanzaNode> nodes = message.stanza.get_all_subnodes();
+ Gee.List<StanzaNode> nodes = message.stanza.get_all_subnodes();
foreach (StanzaNode node in nodes) {
if (node.ns_uri == NS_URI && node.name in MARKERS) {
marker_received(stream, message.from, node.name, node.get_attribute("id", NS_URI));