1 /* iksemel (XML parser for Jabber)
2 ** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
3 ** This code is free software; you can redistribute it and/or
4 ** modify it under the terms of GNU Lesser General Public License.
5 */
6 
7 #include "common.h"
8 #include "iksemel.h"
9 
10 iksid *
iks_id_new(ikstack * s,const char * jid)11 iks_id_new (ikstack *s, const char *jid)
12 {
13 	iksid *id;
14 	char *src, *tmp;
15 
16 /* FIXME: add jabber id validity checks to this function */
17 /* which characters are allowed in id parts? */
18 
19 	if (!jid) return NULL;
20 	id = iks_stack_alloc (s, sizeof (iksid));
21 	if (!id) return NULL;
22 	memset (id, 0, sizeof (iksid));
23 
24 	/* skip scheme */
25 	if (strncmp ("jabber:", jid, 7) == 0) jid += 7;
26 
27 	id->full = iks_stack_strdup (s, jid, 0);
28 	src = id->full;
29 
30 	/* split resource */
31 	tmp = strchr (src, '/');
32 	if (tmp) {
33 		id->partial = iks_stack_strdup (s, src, tmp - src);
34 		id->resource = tmp + 1;
35 		src = id->partial;
36 	} else {
37 		id->partial = src;
38 	}
39 
40 	/* split user */
41 	tmp = strchr (src, '@');
42 	if (tmp) {
43 		id->user = iks_stack_strdup (s, src, tmp - src);
44 		src = ++tmp;
45 	}
46 
47 	id->server = src;
48 
49 	return id;
50 }
51 
52 int
iks_id_cmp(iksid * a,iksid * b,int parts)53 iks_id_cmp (iksid *a, iksid *b, int parts)
54 {
55 	int diff;
56 
57 	if (!a || !b) return (IKS_ID_RESOURCE | IKS_ID_USER | IKS_ID_SERVER);
58 	diff = 0;
59 	if (parts & IKS_ID_RESOURCE && !(!a->resource && !b->resource) && iks_strcmp (a->resource, b->resource) != 0)
60 		diff += IKS_ID_RESOURCE;
61 	if (parts & IKS_ID_USER && !(!a->user && !b->user) && iks_strcasecmp (a->user, b->user) != 0)
62 		diff += IKS_ID_USER;
63 	if (parts & IKS_ID_SERVER && !(!a->server && !b->server) && iks_strcmp (a->server, b->server) != 0)
64 		diff += IKS_ID_SERVER;
65 	return diff;
66 }
67 
68 ikspak *
iks_packet(iks * x)69 iks_packet (iks *x)
70 {
71 	ikspak *pak;
72 	ikstack *s;
73 	char *tmp;
74 
75 	s = iks_stack (x);
76 	pak = iks_stack_alloc (s, sizeof (ikspak));
77 	if (!pak) return NULL;
78 	memset (pak, 0, sizeof (ikspak));
79 	pak->x = x;
80 	tmp = iks_find_attrib (x, "from");
81 	if (tmp) pak->from = iks_id_new (s, tmp);
82 	pak->id = iks_find_attrib (x, "id");
83 
84 	tmp = iks_find_attrib (x, "type");
85 	if (strcmp (iks_name (x), "message") == 0) {
86 		pak->type = IKS_PAK_MESSAGE;
87 		if (tmp) {
88 			if (strcmp (tmp, "chat") == 0)
89 				pak->subtype = IKS_TYPE_CHAT;
90 			else if (strcmp (tmp, "groupchat") == 0)
91 				pak->subtype = IKS_TYPE_GROUPCHAT;
92 			else if (strcmp (tmp, "headline") == 0)
93 				pak->subtype = IKS_TYPE_HEADLINE;
94 			else if (strcmp (tmp, "error") == 0)
95 				pak->subtype = IKS_TYPE_ERROR;
96 		}
97 	} else if (strcmp (iks_name (x), "presence") == 0) {
98 		pak->type = IKS_PAK_S10N;
99 		if (tmp) {
100 			if (strcmp (tmp, "unavailable") == 0) {
101 				pak->type = IKS_PAK_PRESENCE;
102 				pak->subtype = IKS_TYPE_UNAVAILABLE;
103 				pak->show = IKS_SHOW_UNAVAILABLE;
104 			} else if (strcmp (tmp, "probe") == 0) {
105 				pak->type = IKS_PAK_PRESENCE;
106 				pak->subtype = IKS_TYPE_PROBE;
107 			} else if(strcmp(tmp, "subscribe") == 0)
108 				pak->subtype = IKS_TYPE_SUBSCRIBE;
109 			else if(strcmp(tmp, "subscribed") == 0)
110 				pak->subtype = IKS_TYPE_SUBSCRIBED;
111 			else if(strcmp(tmp, "unsubscribe") == 0)
112 				pak->subtype = IKS_TYPE_UNSUBSCRIBE;
113 			else if(strcmp(tmp, "unsubscribed") == 0)
114 				pak->subtype = IKS_TYPE_UNSUBSCRIBED;
115 			else if(strcmp(tmp, "error") == 0)
116 				pak->subtype = IKS_TYPE_ERROR;
117 		} else {
118 			pak->type = IKS_PAK_PRESENCE;
119 			pak->subtype = IKS_TYPE_AVAILABLE;
120 			tmp = iks_find_cdata (x, "show");
121 			pak->show = IKS_SHOW_AVAILABLE;
122 			if (tmp) {
123 				if (strcmp (tmp, "chat") == 0)
124 					pak->show = IKS_SHOW_CHAT;
125 				else if (strcmp (tmp, "away") == 0)
126 					pak->show = IKS_SHOW_AWAY;
127 				else if (strcmp (tmp, "xa") == 0)
128 					pak->show = IKS_SHOW_XA;
129 				else if (strcmp (tmp, "dnd") == 0)
130 					pak->show = IKS_SHOW_DND;
131 			}
132 		}
133 	} else if (strcmp (iks_name (x), "iq") == 0) {
134 		iks *q;
135 		pak->type = IKS_PAK_IQ;
136 		if (tmp) {
137 			if (strcmp (tmp, "get") == 0)
138 				pak->subtype = IKS_TYPE_GET;
139 			else if (strcmp (tmp, "set") == 0)
140 				pak->subtype = IKS_TYPE_SET;
141 			else if (strcmp (tmp, "result") == 0)
142 				pak->subtype = IKS_TYPE_RESULT;
143 			else if (strcmp (tmp, "error") == 0)
144 				pak->subtype = IKS_TYPE_ERROR;
145 		}
146 		for (q = iks_child (x); q; q = iks_next (q)) {
147 			if (IKS_TAG == iks_type (q)) {
148 				char *ns;
149 				ns = iks_find_attrib (q, "xmlns");
150 				if (ns) {
151 					pak->query = q;
152 					pak->ns = ns;
153 					break;
154 				}
155 			}
156 		}
157 	}
158 	return pak;
159 }
160 
161 iks *
iks_make_auth(iksid * id,const char * pass,const char * sid)162 iks_make_auth (iksid *id, const char *pass, const char *sid)
163 {
164 	iks *x, *y;
165 
166 	x = iks_new ("iq");
167 	iks_insert_attrib (x, "type", "set");
168 	y = iks_insert (x, "query");
169 	iks_insert_attrib (y, "xmlns", IKS_NS_AUTH);
170 	iks_insert_cdata (iks_insert (y, "username"), id->user, 0);
171 	iks_insert_cdata (iks_insert (y, "resource"), id->resource, 0);
172 	if(sid) {
173 		char buf[41];
174 		iksha *sha;
175 		sha = iks_sha_new ();
176 		iks_sha_hash (sha, (const unsigned char*)sid, strlen (sid), 0);
177 		iks_sha_hash (sha, (const unsigned char*)pass, strlen (pass), 1);
178 		iks_sha_print (sha, buf);
179 		iks_sha_delete (sha);
180 		iks_insert_cdata (iks_insert (y, "digest"), buf, 40);
181 	} else {
182 		iks_insert_cdata (iks_insert (y, "password"), pass, 0);
183 	}
184 	return x;
185 }
186 
187 iks *
iks_make_msg(enum iksubtype type,const char * to,const char * body)188 iks_make_msg (enum iksubtype type, const char *to, const char *body)
189 {
190 	iks *x;
191 	char *t = NULL;
192 
193 	x = iks_new ("message");
194 	switch (type) {
195 	case IKS_TYPE_CHAT: t = "chat"; break;
196 	case IKS_TYPE_GROUPCHAT: t = "groupchat"; break;
197 	case IKS_TYPE_HEADLINE: t = "headline"; break;
198 	default: break;
199 	}
200 	if (t) iks_insert_attrib (x, "type", t);
201 	if (to) iks_insert_attrib (x, "to", to);
202 	if (body) iks_insert_cdata (iks_insert (x, "body"), body, 0);
203 	return x;
204 }
205 
206 iks *
iks_make_s10n(enum iksubtype type,const char * to,const char * msg)207 iks_make_s10n (enum iksubtype type, const char *to, const char *msg)
208 {
209 	iks *x;
210 	char *t;
211 
212 	x = iks_new ("presence");
213 	switch (type) {
214 		case IKS_TYPE_SUBSCRIBE: t = "subscribe"; break;
215 		case IKS_TYPE_SUBSCRIBED: t = "subscribed"; break;
216 		case IKS_TYPE_UNSUBSCRIBE: t = "unsubscribe"; break;
217 		case IKS_TYPE_UNSUBSCRIBED: t = "unsubscribed"; break;
218 		case IKS_TYPE_PROBE: t = "probe"; break;
219 		default: t = NULL; break;
220 	}
221 	if (t) iks_insert_attrib (x, "type", t);
222 	if (to) iks_insert_attrib (x, "to", to);
223 	if (msg) iks_insert_cdata(iks_insert (x, "status"), msg, 0);
224 	return x;
225 }
226 
227 iks *
iks_make_pres(enum ikshowtype show,const char * status)228 iks_make_pres (enum ikshowtype show, const char *status)
229 {
230 	iks *x;
231 	char *t;
232 
233 	x = iks_new ("presence");
234 	switch (show) {
235 		case IKS_SHOW_CHAT: t = "chat"; break;
236 		case IKS_SHOW_AWAY: t = "away"; break;
237 		case IKS_SHOW_XA: t = "xa"; break;
238 		case IKS_SHOW_DND: t = "dnd"; break;
239 		case IKS_SHOW_UNAVAILABLE:
240 			t = NULL;
241 			iks_insert_attrib (x, "type", "unavailable");
242 			break;
243 		default: t = NULL; break;
244 	}
245 	if (t) iks_insert_cdata (iks_insert (x, "show"), t, 0);
246 	if (status) iks_insert_cdata(iks_insert (x, "status"), status, 0);
247 	return x;
248 }
249 
250 iks *
iks_make_iq(enum iksubtype type,const char * xmlns)251 iks_make_iq (enum iksubtype type, const char *xmlns)
252 {
253 	iks *x;
254 	char *t = NULL;
255 
256 	x = iks_new ("iq");
257 	switch (type) {
258 	case IKS_TYPE_GET: t = "get"; break;
259 	case IKS_TYPE_SET: t = "set"; break;
260 	case IKS_TYPE_RESULT: t = "result"; break;
261 	case IKS_TYPE_ERROR: t = "error"; break;
262 	default: break;
263 	}
264 	if (t) iks_insert_attrib (x, "type", t);
265 	iks_insert_attrib (iks_insert (x, "query"), "xmlns", xmlns);
266 
267 	return x;
268 }
269 
270 iks *
iks_make_resource_bind(iksid * id)271 iks_make_resource_bind (iksid *id)
272 {
273 	iks *x, *y, *z;
274 
275 	x = iks_new("iq");
276 	iks_insert_attrib(x, "type", "set");
277 	y = iks_insert(x, "bind");
278 	iks_insert_attrib(y, "xmlns", IKS_NS_XMPP_BIND);
279 	if (id->resource && iks_strcmp(id->resource, "")) {
280 		z = iks_insert(y, "resource");
281 		iks_insert_cdata(z, id->resource, 0);
282 	}
283 	return x;
284 }
285 
286 iks *
iks_make_session(void)287 iks_make_session (void)
288 {
289 	iks *x, *y;
290 
291 	x = iks_new ("iq");
292 	iks_insert_attrib (x, "type", "set");
293 	y = iks_insert (x, "session");
294 	iks_insert_attrib (y, "xmlns", IKS_NS_XMPP_SESSION);
295 	return x;
296 }
297 
298 static int
iks_sasl_mechanisms(iks * x)299 iks_sasl_mechanisms (iks *x)
300 {
301 	int sasl_mech = 0;
302 
303 	while (x) {
304 		if (!iks_strcmp(iks_cdata(iks_child(x)), "DIGEST-MD5"))
305 			sasl_mech |= IKS_STREAM_SASL_MD5;
306 		else if (!iks_strcmp(iks_cdata(iks_child(x)), "PLAIN"))
307 			sasl_mech |= IKS_STREAM_SASL_PLAIN;
308 		x = iks_next_tag(x);
309 	}
310 	return sasl_mech;
311 }
312 
313 int
iks_stream_features(iks * x)314 iks_stream_features (iks *x)
315 {
316 	int features = 0;
317 
318 	if (iks_strcmp(iks_name(x), "stream:features"))
319 		return 0;
320 	for (x = iks_child(x); x; x = iks_next_tag(x))
321 		if (!iks_strcmp(iks_name(x), "starttls"))
322 			features |= IKS_STREAM_STARTTLS;
323 		else if (!iks_strcmp(iks_name(x), "bind"))
324 			features |= IKS_STREAM_BIND;
325 		else if (!iks_strcmp(iks_name(x), "session"))
326 			features |= IKS_STREAM_SESSION;
327 		else if (!iks_strcmp(iks_name(x), "mechanisms"))
328 			features |= iks_sasl_mechanisms(iks_child(x));
329 	return features;
330 }
331