struct xmpp_event *xmpp_queue = NULL;
-/* We have just received a <stream> tag from the client, so send them ours */
+/*
+ * Given a source string and a target buffer, returns the string
+ * properly escaped for insertion into an XML stream. Returns a
+ * pointer to the target buffer for convenience.
+ *
+ * BUG: this does not properly handle UTF-8
+ */
+char *xmlesc(char *buf, char *str, int bufsiz)
+{
+ char *ptr;
+ unsigned char ch;
+ int len = 0;
+
+ if (!buf) return(NULL);
+ buf[0] = 0;
+ len = 0;
+ if (!str) {
+ return(buf);
+ }
+
+ for (ptr=str; *ptr; ptr++) {
+ ch = *ptr;
+ if (ch == '<') {
+ strcpy(&buf[len], "<");
+ len += 4;
+ }
+ else if (ch == '>') {
+ strcpy(&buf[len], ">");
+ len += 4;
+ }
+ else if (ch == '&') {
+ strcpy(&buf[len], "&");
+ len += 5;
+ }
+ else if (ch <= 0x7F) {
+ buf[len++] = ch;
+ buf[len] = 0;
+ }
+ else if (ch > 0x7F) {
+ char oct[10];
+ sprintf(oct, "&#%o;", ch);
+ strcpy(&buf[len], oct);
+ len += strlen(oct);
+ }
+ if ((len + 6) > bufsiz) {
+ return(buf);
+ }
+ }
+ return(buf);
+}
+
+/*
+ * We have just received a <stream> tag from the client, so send them ours
+ */
void xmpp_stream_start(void *data, const char *supplied_el, const char **attr)
{
+ char xmlbuf[256];
+
while (*attr) {
if (!strcasecmp(attr[0], "to")) {
safestrncpy(XMPP->server_name, attr[1], sizeof XMPP->server_name);
cprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
cprintf("<stream:stream ");
- cprintf("from=\"%s\" ", XMPP->server_name);
+ cprintf("from=\"%s\" ", xmlesc(xmlbuf, XMPP->server_name, sizeof xmlbuf));
cprintf("id=\"%08x\" ", CC->cs_pid);
cprintf("version=\"1.0\" ");
cprintf("xmlns:stream=\"http://etherx.jabber.org/streams\" ");
void xmpp_xml_end(void *data, const char *supplied_el) {
char el[256];
char *sep = NULL;
+ char xmlbuf[256];
/* Axe the namespace, we don't care about it */
safestrncpy(el, supplied_el, sizeof el);
else if (XMPP->ping_requested) {
cprintf("<iq type=\"result\" ");
if (!IsEmptyStr(XMPP->iq_from)) {
- cprintf("to=\"%s\" ", XMPP->iq_from);
+ cprintf("to=\"%s\" ", xmlesc(xmlbuf, XMPP->iq_from, sizeof xmlbuf));
}
if (!IsEmptyStr(XMPP->iq_to)) {
- cprintf("from=\"%s\" ", XMPP->iq_to);
+ cprintf("from=\"%s\" ", xmlesc(xmlbuf, XMPP->iq_to, sizeof xmlbuf));
}
- cprintf("id=\"%s\"/>", XMPP->iq_id);
+ cprintf("id=\"%s\"/>", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
}
/*
"Unknown query <%s> - returning <service-unavailable/>\n",
el
);
- cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_id);
+ cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
cprintf("<error code=\"503\" type=\"cancel\">"
"<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
"</error>"
/* Tell the client what its JID is */
- cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_id);
+ cprintf("<iq type=\"result\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
cprintf("<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\">");
- cprintf("<jid>%s</jid>", XMPP->client_jid);
+ cprintf("<jid>%s</jid>", xmlesc(xmlbuf, XMPP->client_jid, sizeof xmlbuf));
cprintf("</bind>");
cprintf("</iq>");
}
else if (XMPP->iq_session) {
- cprintf("<iq type=\"result\" id=\"%s\">", XMPP->iq_id);
+ cprintf("<iq type=\"result\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
cprintf("</iq>");
}
else {
- cprintf("<iq type=\"error\" id=\"%s\">", XMPP->iq_id);
- cprintf("<error>Don't know howto do '%s'!</error>", XMPP->iq_type);
+ cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, XMPP->iq_id, sizeof xmlbuf));
+ cprintf("<error>Don't know howto do '%s'!</error>", xmlesc(xmlbuf, XMPP->iq_type, sizeof xmlbuf));
cprintf("</iq>");
}
void xmpp_massacre_roster(void);
void xmpp_delete_old_buddies_who_no_longer_exist_from_the_client_roster(void);
int xmpp_is_visible(struct CitContext *from, struct CitContext *to_whom);
+char *xmlesc(char *buf, char *str, int bufsiz);
void xmpp_output_incoming_messages(void) {
struct ExpressMessage *ptr;
+ char xmlbuf1[4096];
+ char xmlbuf2[4096];
while (CC->FirstExpressMessage != NULL) {
end_critical_section(S_SESSION_TABLE);
cprintf("<message to=\"%s\" from=\"%s\" type=\"chat\">",
- XMPP->client_jid,
- ptr->sender_email);
+ xmlesc(xmlbuf1, XMPP->client_jid, sizeof xmlbuf1),
+ xmlesc(xmlbuf2, ptr->sender_email, sizeof xmlbuf2)
+ );
if (ptr->text != NULL) {
striplt(ptr->text);
- cprintf("<body>%s</body>", ptr->text);
+ cprintf("<body>%s</body>", xmlesc(xmlbuf1, ptr->text, sizeof xmlbuf1));
free(ptr->text);
}
cprintf("</message>");
*/
void xmpp_indicate_presence(char *presence_jid)
{
- cprintf("<presence from=\"%s\" to=\"%s\"></presence>",
- presence_jid,
- XMPP->client_jid
- );
+ char xmlbuf[256];
+
+ cprintf("<presence from=\"%s\" ", xmlesc(xmlbuf, presence_jid, sizeof xmlbuf));
+ cprintf("to=\"%s\"></presence>", xmlesc(xmlbuf, XMPP->client_jid, sizeof xmlbuf));
}
*/
void xmpp_destroy_buddy(char *presence_jid) {
static int unsolicited_id = 1;
+ char xmlbuf1[256];
+ char xmlbuf2[256];
if (!presence_jid) return;
if (!XMPP) return;
/* Transmit non-presence information */
cprintf("<presence type=\"unavailable\" from=\"%s\" to=\"%s\"></presence>",
- presence_jid, XMPP->client_jid
+ xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1),
+ xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2)
);
cprintf("<presence type=\"unsubscribed\" from=\"%s\" to=\"%s\"></presence>",
- presence_jid, XMPP->client_jid
+ xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1),
+ xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2)
);
// FIXME ... we should implement xmpp_indicate_nonpresence so we can use it elsewhere
/* Do an unsolicited roster update that deletes the contact. */
cprintf("<iq from=\"%s\" to=\"%s\" id=\"unbuddy_%x\" type=\"result\">",
- CC->cs_inet_email,
- XMPP->client_jid,
+ xmlesc(xmlbuf1, CC->cs_inet_email, sizeof xmlbuf1),
+ xmlesc(xmlbuf2, XMPP->client_jid, sizeof xmlbuf2),
++unsolicited_id
);
cprintf("<query xmlns=\"jabber:iq:roster\">");
- cprintf("<item jid=\"%s\" subscription=\"remove\">", presence_jid);
- cprintf("<group>%s</group>", config.c_humannode);
+ cprintf("<item jid=\"%s\" subscription=\"remove\">", xmlesc(xmlbuf1, presence_jid, sizeof xmlbuf1));
+ cprintf("<group>%s</group>", xmlesc(xmlbuf1, config.c_humannode, sizeof xmlbuf1));
cprintf("</item>");
cprintf("</query>"
"</iq>"
* Output a single roster item, for roster queries or pushes
*/
void xmpp_roster_item(struct CitContext *cptr) {
+ char xmlbuf1[256];
+ char xmlbuf2[256];
+
cprintf("<item jid=\"%s\" name=\"%s\" subscription=\"both\">",
- cptr->cs_inet_email,
- cptr->user.fullname
+ xmlesc(xmlbuf1, cptr->cs_inet_email, sizeof xmlbuf1),
+ xmlesc(xmlbuf2, cptr->user.fullname, sizeof xmlbuf2)
);
- cprintf("<group>%s</group>", config.c_humannode);
+ cprintf("<group>%s</group>", xmlesc(xmlbuf1, config.c_humannode, sizeof xmlbuf1));
cprintf("</item>");
}
{
int supported_namespace = 0;
int roster_query = 0;
+ char xmlbuf[256];
/* We need to know before we begin the response whether this is a supported namespace, so
* unfortunately all supported namespaces need to be defined here *and* down below where
cprintf("<iq type=\"error\" ");
}
if (!IsEmptyStr(iq_from)) {
- cprintf("to=\"%s\" ", iq_from);
+ cprintf("to=\"%s\" ", xmlesc(xmlbuf, iq_from, sizeof xmlbuf));
}
- cprintf("id=\"%s\">", iq_id);
+ cprintf("id=\"%s\">", xmlesc(xmlbuf, iq_id, sizeof xmlbuf));
/*
* Is this a query we know how to handle?
*/
void xmpp_non_sasl_authenticate(char *iq_id, char *username, char *password, char *resource) {
int result;
+ char xmlbuf[256];
if (CC->logged_in) CtdlUserLogout(); /* Client may try to log in twice. Handle this. */
if (result == login_ok) {
result = CtdlTryPassword(password);
if (result == pass_ok) {
- cprintf("<iq type=\"result\" id=\"%s\"></iq>", iq_id); /* success */
+ cprintf("<iq type=\"result\" id=\"%s\"></iq>", xmlesc(xmlbuf, iq_id, sizeof xmlbuf)); /* success */
return;
}
}
/* failure */
- cprintf("<iq type=\"error\" id=\"%s\">", iq_id);
+ cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, iq_id, sizeof xmlbuf));
cprintf("<error code=\"401\" type=\"auth\">"
"<not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
"</error>"