summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2020-03-28 23:48:02 +0300
committerdefanor <defanor@uberspace.net>2020-03-28 23:48:02 +0300
commit7889b698476397b60e0d43e7c498dd81ae2eb95e (patch)
treec7902fbdf8fcb76f88db886fdb4b8023c124f6b6
parent3869cb3f33d2694fa94b7a6d45b52c9a7568a2a2 (diff)
Handle service discovery requests
-rw-r--r--README10
-rw-r--r--src/rexmpp.c57
-rw-r--r--src/rexmpp.h3
3 files changed, 63 insertions, 7 deletions
diff --git a/README b/README
index fd1723f..3a77d6e 100644
--- a/README
+++ b/README
@@ -56,16 +56,14 @@ A rough roadmap:
- Primary IM features:
-[.] XMPP IM (RFC 6121): loading and managing of the roster, possibly
- keeping track of contact presences. Optional roster management
- (loading and updates on roster push, with versioning) is
- implemented.
+[.] XMPP IM (RFC 6121): roster management is implemented, but not keeping
+ track of contact presences yet.
- Common and reliable IM features:
-[.] XEP-0030: Service Discovery (implemented partially, just to
- discover server features)
+[.] XEP-0030: Service Discovery: implemented, but without tracking of
+ features provided by known entities yet.
[ ] XEP-0115: Entity Capabilities
[ ] XEP-0045: Multi-User Chat
[ ] XEP-0166: Jingle
diff --git a/src/rexmpp.c b/src/rexmpp.c
index d2d3dd8..776069a 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -52,6 +52,34 @@ void rexmpp_log (rexmpp_t *s, int priority, const char *format, ...)
}
}
+xmlNodePtr rexmpp_xml_feature (const char *var) {
+ xmlNodePtr feature = xmlNewNode(NULL, "feature");
+ xmlNewProp(feature, "var", var);
+ return feature;
+}
+
+xmlNodePtr rexmpp_xml_error (const char *type, const char *condition) {
+ xmlNodePtr error = xmlNewNode(NULL, "error");
+ xmlNewProp(error, "type", type);
+ xmlNodePtr cond = xmlNewNode(NULL, condition);
+ xmlNewNs(cond, "urn:ietf:params:xml:ns:xmpp-stanzas", NULL);
+ xmlAddChild(error, cond);
+ return error;
+}
+
+xmlNodePtr rexmpp_xml_default_disco_info () {
+ /* There must be at least one identity, so filling in somewhat
+ sensible defaults. A basic client may leave them be, while an
+ advanced one would adjust and/or extend them. */
+ xmlNodePtr identity = xmlNewNode(NULL, "identity");
+ xmlNewProp(identity, "category", "client");
+ xmlNewProp(identity, "type", "console");
+ xmlNewProp(identity, "name", "rexmpp");
+ xmlNodePtr disco_feature = rexmpp_xml_feature("http://jabber.org/protocol/disco#info");
+ identity->next = disco_feature;
+ return identity;
+}
+
rexmpp_err_t rexmpp_init (rexmpp_t *s,
const char *jid,
log_function_t log_function,
@@ -174,6 +202,8 @@ rexmpp_err_t rexmpp_init (rexmpp_t *s,
}
gsasl_callback_set(s->sasl_ctx, s->sasl_property_cb);
+ s->disco_info = rexmpp_xml_default_disco_info();
+
return REXMPP_SUCCESS;
}
@@ -253,6 +283,10 @@ void rexmpp_done (rexmpp_t *s) {
free(s->roster_ver);
s->roster_ver = NULL;
}
+ if (s->disco_info != NULL) {
+ xmlFreeNodeList(s->disco_info);
+ s->disco_info = NULL;
+ }
while (s->stanza_queue != NULL) {
xmlNodePtr next = xmlNextElementSibling(s->stanza_queue);
xmlFreeNode(s->send_queue);
@@ -1156,7 +1190,10 @@ void rexmpp_stream_bind (rexmpp_t *s) {
void rexmpp_process_element (rexmpp_t *s) {
xmlNodePtr elem = s->current_element;
- /* IQs */
+ /* IQs. These are the ones that should be processed by the library;
+ if a user-facing application wants to handle them on its own, it
+ should cancel further processing by the library (so we can send
+ errors for unhandled IQs here). */
if (rexmpp_xml_match(elem, "jabber:client", "iq")) {
char *type = xmlGetProp(elem, "type");
/* IQ responses. */
@@ -1228,6 +1265,24 @@ void rexmpp_process_element (rexmpp_t *s) {
if (s->roster_cache_file != NULL) {
rexmpp_roster_cache_write(s);
}
+ } else {
+ /* An unknown request. */
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "service-unavailable"));
+ }
+ }
+ /* IQ "get" requests. */
+ if (strcmp(type, "get") == 0) {
+ xmlNodePtr query = xmlFirstElementChild(elem);
+ if (rexmpp_xml_match(query, "http://jabber.org/protocol/disco#info", "query")) {
+ xmlNodePtr result = xmlNewNode(NULL, "query");
+ xmlNewNs(result, "http://jabber.org/protocol/disco#info", NULL);
+ xmlAddChild(result, xmlCopyNodeList(s->disco_info));
+ rexmpp_iq_reply(s, elem, "result", result);
+ } else {
+ /* An unknown request. */
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "service-unavailable"));
}
}
free(type);
diff --git a/src/rexmpp.h b/src/rexmpp.h
index 7679546..f064154 100644
--- a/src/rexmpp.h
+++ b/src/rexmpp.h
@@ -252,6 +252,9 @@ struct rexmpp
xmlNodePtr roster_items;
char *roster_ver;
+ /* Other dynamic data. */
+ xmlNodePtr disco_info;
+
/* IQs we're waiting for responses to. */
rexmpp_iq_t *active_iq;