DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
xmpp.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file xmpp.cpp
6 * @brief XMPP stanza codec (see xmpp.h).
7 */
8
10
11#if DETWS_ENABLE_XMPP
12
13#include <string.h>
14
15namespace
16{
17// Append a literal to out[*n], bounded. Returns false on overflow.
18bool put(char *out, size_t cap, size_t *n, const char *s)
19{
20 size_t sl = strnlen(s, cap + 1);
21 if (*n + sl >= cap)
22 return false;
23 memcpy(out + *n, s, sl);
24 *n += sl;
25 return true;
26}
27
28// Append s, XML-escaped, into out[*n]. Returns false on overflow.
29bool put_escaped(char *out, size_t cap, size_t *n, const char *s)
30{
31 for (; *s; s++)
32 {
33 const char *rep = nullptr;
34 switch (*s)
35 {
36 case '&':
37 rep = "&amp;";
38 break;
39 case '<':
40 rep = "&lt;";
41 break;
42 case '>':
43 rep = "&gt;";
44 break;
45 case '\'':
46 rep = "&apos;";
47 break;
48 case '"':
49 rep = "&quot;";
50 break;
51 default:
52 break;
53 }
54 if (rep)
55 {
56 if (!put(out, cap, n, rep))
57 return false;
58 }
59 else
60 {
61 if (*n + 1 >= cap)
62 return false;
63 out[(*n)++] = *s;
64 }
65 }
66 return true;
67}
68
69// Append `attr="value"` (value escaped) when value is non-null.
70bool put_attr(char *out, size_t cap, size_t *n, const char *attr, const char *value)
71{
72 if (!value)
73 return true;
74 return put(out, cap, n, " ") && put(out, cap, n, attr) && put(out, cap, n, "=\"") &&
75 put_escaped(out, cap, n, value) && put(out, cap, n, "\"");
76}
77
78size_t finish(char *out, size_t n, bool ok)
79{
80 if (!ok)
81 return 0;
82 out[n] = '\0';
83 return n;
84}
85} // namespace
86
87size_t detws_xmpp_escape(const char *in, size_t in_len, char *out, size_t cap)
88{
89 if (!in || !out)
90 return 0;
91 size_t n = 0;
92 for (size_t i = 0; i < in_len; i++)
93 {
94 char one[2] = {in[i], '\0'};
95 if (!put_escaped(out, cap, &n, one))
96 return 0;
97 }
98 return finish(out, n, true);
99}
100
101size_t detws_xmpp_stream_open(const char *from, const char *to, char *out, size_t cap)
102{
103 size_t n = 0;
104 bool ok =
105 put(out, cap, &n, "<?xml version='1.0'?><stream:stream") && put_attr(out, cap, &n, "from", from) &&
106 put_attr(out, cap, &n, "to", to) &&
107 put(out, cap, &n, " xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
108 return finish(out, n, ok);
109}
110
111size_t detws_xmpp_message(const char *to, const char *from, const char *type, const char *body, char *out, size_t cap)
112{
113 size_t n = 0;
114 bool ok = put(out, cap, &n, "<message") && put_attr(out, cap, &n, "to", to) &&
115 put_attr(out, cap, &n, "from", from) && put_attr(out, cap, &n, "type", type) && put(out, cap, &n, ">");
116 if (ok && body)
117 ok = put(out, cap, &n, "<body>") && put_escaped(out, cap, &n, body) && put(out, cap, &n, "</body>");
118 ok = ok && put(out, cap, &n, "</message>");
119 return finish(out, n, ok);
120}
121
122size_t detws_xmpp_presence(const char *type, char *out, size_t cap)
123{
124 size_t n = 0;
125 bool ok = put(out, cap, &n, "<presence") && put_attr(out, cap, &n, "type", type) && put(out, cap, &n, "/>");
126 return finish(out, n, ok);
127}
128
129size_t detws_xmpp_iq(const char *type, const char *id, const char *child_xml, char *out, size_t cap)
130{
131 size_t n = 0;
132 bool ok = put(out, cap, &n, "<iq") && put_attr(out, cap, &n, "type", type) && put_attr(out, cap, &n, "id", id) &&
133 put(out, cap, &n, ">");
134 if (ok && child_xml)
135 ok = put(out, cap, &n, child_xml);
136 ok = ok && put(out, cap, &n, "</iq>");
137 return finish(out, n, ok);
138}
139
140size_t detws_xmpp_stanza_name(const char *xml, size_t len, char *out, size_t cap)
141{
142 if (!xml || !out || cap == 0)
143 return 0;
144 for (size_t i = 0; i + 1 < len; i++)
145 {
146 if (xml[i] != '<')
147 continue;
148 char c = xml[i + 1];
149 if (c == '?' || c == '!' || c == '/') // skip declaration / comment / close tag
150 continue;
151 size_t j = i + 1, k = 0;
152 while (j < len && xml[j] != ' ' && xml[j] != '>' && xml[j] != '/' && xml[j] != '\t' && xml[j] != '\n')
153 {
154 if (k + 1 >= cap)
155 return 0;
156 out[k++] = xml[j++];
157 }
158 out[k] = '\0';
159 return k;
160 }
161 return 0;
162}
163
164size_t detws_xmpp_attr(const char *xml, size_t len, const char *attr, char *out, size_t cap)
165{
166 if (!xml || !attr || !out || cap == 0)
167 return 0;
168 size_t al = strnlen(attr, len + 1);
169 // Search only within the first start tag (up to the first '>').
170 size_t end = 0;
171 while (end < len && xml[end] != '>')
172 end++;
173 for (size_t i = 0; i + al + 2 < end; i++)
174 {
175 // match <space>attr= (leading space avoids a substring hit inside another attr name)
176 if (!((i == 0 || xml[i - 1] == ' ') && strncmp(xml + i, attr, al) == 0 && xml[i + al] == '='))
177 continue;
178 char q = xml[i + al + 1];
179 if (q != '"' && q != '\'')
180 return 0;
181 size_t j = i + al + 2, k = 0;
182 while (j < end && xml[j] != q)
183 {
184 if (k + 1 >= cap)
185 return 0;
186 out[k++] = xml[j++];
187 }
188 out[k] = '\0';
189 return k;
190 }
191 return 0;
192}
193
194#endif // DETWS_ENABLE_XMPP
XMPP (RFC 6120) stanza codec (DETWS_ENABLE_XMPP).