1 /* iksemel (XML parser for Jabber)
2 ** Copyright (C) 2011 Gurer Ozen
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 "exceptions.h"
8 #include "document.h"
9 #include "stream.h"
10
11 typedef struct {
12 PyObject_HEAD
13 PyObject *jid;
14 iksparser *parser;
15 int features;
16 int authorized;
17 int use_sasl;
18 int use_tls;
19 } Stream;
20
21 static int Stream_init(Stream *self, PyObject *args, PyObject *kwargs);
22 static PyObject *Stream_connect(Stream *self, PyObject *args, PyObject *kwargs);
23 static PyObject *Stream_fileno(Stream *self);
24 static PyObject *Stream_send(Stream *self, PyObject *args);
25 static PyObject *Stream_recv(Stream *self);
26 static void Stream_dealloc(Stream *self);
27
28 static PyMethodDef Stream_methods[] = {
29 { "connect", (PyCFunction) Stream_connect, METH_KEYWORDS, "Connect stream to an XMPP server" },
30 { "fileno", (PyCFunction) Stream_fileno, METH_NOARGS, "Return file descriptor of the socket" },
31 { "send", (PyCFunction) Stream_send, METH_VARARGS, "Send stanzas to server" },
32 { "recv", (PyCFunction) Stream_recv, METH_NOARGS, "Read data from server" },
33 { NULL }
34 };
35
36 static PyTypeObject Stream_type = {
37 PyObject_HEAD_INIT(NULL)
38 0, /* ob_size */
39 "iksemel.Stream", /* tp_name */
40 sizeof(Stream), /* tp_basicsize */
41 0, /* tp_itemsize */
42 (destructor)Stream_dealloc, /* tp_dealloc */
43 0, /* tp_print */
44 0, /* tp_getattr */
45 0, /* tp_setattr */
46 0, /* tp_compare */
47 0, /* tp_repr */
48 0, /* tp_as_number */
49 0, /* tp_as_sequence */
50 0, /* tp_as_mapping */
51 0, /* tp_hash */
52 0, /* tp_call */
53 0, /* tp_str */
54 0, /* tp_getattro */
55 0, /* tp_setattro */
56 0, /* tp_as_buffer */
57 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
58 "iksemel stream object", /* tp_doc */
59 0, /* tp_traverse */
60 0, /* tp_clear */
61 0, /* tp_richcompare */
62 0, /* tp_weaklistoffset */
63 0, /* tp_iter */
64 0, /* tp_iternext */
65 Stream_methods, /* tp_methods */
66 0, /* tp_members */
67 0, /* tp_getset */
68 0, /* tp_base */
69 0, /* tp_dict */
70 0, /* tp_descr_get */
71 0, /* tp_descr_set */
72 0, /* tp_dictoffset */
73 (initproc)Stream_init, /* tp_init */
74 0, /* tp_alloc */
75 0 /* tp_new */
76 };
77
78 static void
start_sasl(Stream * self,enum ikssasltype type)79 start_sasl(Stream *self, enum ikssasltype type)
80 {
81 PyObject *hook;
82 PyObject *ret;
83 PyObject *o;
84 char *local;
85 char *pw;
86
87 o = PyObject_GetAttrString(self->jid, "local");
88 if (!o) return;
89 local = PyString_AsString(o);
90 if (!local) {
91 Py_DECREF(o);
92 return;
93 }
94
95 hook = PyObject_GetAttrString((PyObject *) self, "ask_password");
96 if (!hook) {
97 Py_DECREF(o);
98 return;
99 }
100 ret = PyObject_CallObject(hook, NULL);
101 Py_DECREF(hook);
102 if (!ret) {
103 Py_DECREF(o);
104 return;
105 }
106 pw = PyString_AsString(ret);
107 if (!pw) {
108 Py_DECREF(ret);
109 Py_DECREF(o);
110 return;
111 }
112
113 iks_start_sasl(self->parser, type, local, pw);
114
115 Py_DECREF(ret);
116 Py_DECREF(o);
117 }
118
119 static void
make_bind(Stream * self)120 make_bind(Stream *self)
121 {
122 iks *x, *y, *z;
123 PyObject *o;
124 char *resource;
125
126 o = PyObject_GetAttrString(self->jid, "resource");
127 if (!o) return;
128 resource = PyString_AsString(o);
129 if (!resource) {
130 PyErr_Clear();
131 resource = "iksemel";
132 }
133
134 x = iks_new("iq");
135 iks_insert_attrib(x, "type", "set");
136 y = iks_insert(x, "bind");
137 iks_insert_attrib(y, "xmlns", IKS_NS_XMPP_BIND);
138 z = iks_insert(y, "resource");
139 iks_insert_cdata(z, resource, 0);
140
141 iks_send(self->parser, x);
142
143 iks_delete(x);
144 Py_DECREF(o);
145 }
146
147 static void
on_features(Stream * self,iks * node)148 on_features(Stream *self, iks *node)
149 {
150 iks *x;
151
152 self->features = iks_stream_features(node);
153 if (self->use_sasl) {
154 if (self->use_tls && !iks_is_secure(self->parser)) return;
155 if (self->authorized) {
156 if (self->features & IKS_STREAM_BIND) {
157 make_bind(self);
158 }
159 if (self->features & IKS_STREAM_SESSION) {
160 x = iks_make_session();
161 iks_insert_attrib(x, "id", "auth");
162 iks_send(self->parser, x);
163 iks_delete(x);
164 }
165 } else {
166 if (self->features & IKS_STREAM_SASL_MD5)
167 start_sasl(self, IKS_SASL_DIGEST_MD5);
168 else if (self->features & IKS_STREAM_SASL_PLAIN)
169 start_sasl(self, IKS_SASL_PLAIN);
170 }
171 }
172 }
173
174 static void
on_success(Stream * self,iks * node)175 on_success(Stream *self, iks *node)
176 {
177 PyObject *o;
178 char *domain;
179
180 o = PyObject_GetAttrString(self->jid, "domain");
181 if (!o) return;
182 domain = PyString_AsString(o);
183 if (!domain) {
184 Py_DECREF(o);
185 return;
186 }
187
188 self->authorized = 1;
189 iks_send_header(self->parser, domain);
190
191 Py_DECREF(o);
192 }
193
194 static int
on_stream(Stream * self,int type,iks * node)195 on_stream(Stream *self, int type, iks *node)
196 {
197 PyObject *hook;
198 PyObject *doc;
199 PyObject *ret;
200 // iks *x;
201
202 switch (type) {
203 case IKS_NODE_NORMAL:
204 if (strcmp("stream:features", iks_name(node)) == 0) {
205 on_features(self, node);
206 } else if (strcmp("success", iks_name(node)) == 0) {
207 on_success(self, node);
208 }
209 #if 0
210 case IKS_NODE_START:
211 if (!iks_is_secure(self->parser)) {
212 iks_start_tls(self->parser);
213 break;
214 }
215 if (!self->use_sasl) {
216 x = iks_make_auth (, , NULL);
217 iks_insert_attrib (x, "id", "auth");
218 iks_send (self->parser, x);
219 iks_delete (x);
220 }
221 break;
222 #endif
223 hook = PyObject_GetAttrString((PyObject *) self, "on_stanza");
224 if (hook) {
225 doc = Document_from_iks(NULL, node);
226 if (!doc) {
227 Py_DECREF(hook);
228 return IKS_NOMEM;
229 }
230 ret = PyObject_CallFunctionObjArgs(hook, doc, NULL);
231 Py_DECREF(hook);
232 // FIXME: handle error
233 if (ret == NULL) {
234 Py_DECREF(ret);
235 return IKS_HOOK;
236 }
237 }
238 }
239 if (node) iks_delete (node);
240 return IKS_OK;
241 }
242
243 void
on_log(Stream * self,const char * data,size_t size,int is_incoming)244 on_log(Stream *self, const char *data, size_t size, int is_incoming)
245 {
246 PyObject *hook;
247 PyObject *args;
248 PyObject *ret;
249 PyObject *b;
250
251 hook = PyObject_GetAttrString((PyObject *) self, "on_xml");
252 if (hook) {
253 b = Py_False;
254 if (is_incoming) b = Py_True;
255 args = Py_BuildValue("s#O", data, size, b);
256 if (args) {
257 ret = PyObject_CallObject(hook, args);
258 if (ret) { Py_DECREF(ret); }
259 Py_DECREF(args);
260 }
261 Py_DECREF(hook);
262 }
263 }
264
265 static int
Stream_init(Stream * self,PyObject * args,PyObject * kwargs)266 Stream_init(Stream *self, PyObject *args, PyObject *kwargs)
267 {
268 PyObject *hook;
269
270 self->jid = NULL;
271 self->parser = iks_stream_new (
272 IKS_NS_CLIENT,
273 self,
274 (iksStreamHook *) on_stream
275 );
276 self->features = 0;
277 self->authorized = 0;
278 self->use_sasl = 1;
279 self->use_tls = 1;
280
281 hook = PyObject_GetAttrString((PyObject *) self, "on_xml");
282 if (hook) {
283 iks_set_log_hook(self->parser, (iksLogHook *) on_log);
284 Py_DECREF(hook);
285 }
286
287 return 0;
288 }
289
290 static PyObject *
Stream_connect(Stream * self,PyObject * args,PyObject * kwargs)291 Stream_connect(Stream *self, PyObject *args, PyObject *kwargs)
292 {
293 PyObject *o;
294 char *host;
295 int e;
296
297 o = PyMapping_GetItemString(kwargs, "tls");
298 if (o) {
299 if (PyObject_IsTrue(o)) {
300 self->use_tls = 1;
301 } else {
302 self->use_tls = 0;
303 }
304 }
305 PyErr_Clear();
306
307 o = PyMapping_GetItemString(kwargs, "sasl");
308 if (o) {
309 if (PyObject_IsTrue(o)) {
310 self->use_sasl = 1;
311 } else {
312 self->use_sasl = 0;
313 }
314 }
315 PyErr_Clear();
316
317 o = PyMapping_GetItemString(kwargs, "jid");
318 if (!o) {
319 PyErr_SetString(PyExc_TypeError, "jid keyword argument is required");
320 return NULL;
321 }
322 Py_INCREF(o);
323 self->jid = o;
324
325 o = PyObject_GetAttrString(self->jid, "domain");
326 if (!o) return NULL;
327 host = PyString_AsString(o);
328 if (!host) {
329 Py_DECREF(o);
330 return NULL;
331 }
332
333 e = iks_connect_tcp(self->parser, host, IKS_JABBER_PORT);
334 if (e) {
335 return exceptions_stream_error(e);
336 }
337
338 Py_DECREF(o);
339
340 Py_INCREF(Py_None);
341 return Py_None;
342 }
343
344 static PyObject *
Stream_fileno(Stream * self)345 Stream_fileno(Stream *self)
346 {
347 return Py_BuildValue("i", iks_fd(self->parser));
348 }
349
350 static PyObject *
Stream_send(Stream * self,PyObject * args)351 Stream_send(Stream *self, PyObject *args)
352 {
353 int e;
354 PyObject *s;
355 char *str;
356
357 if (!PyArg_ParseTuple(args, "O", &s))
358 return NULL;
359
360 s = PyObject_Str(s);
361 if (s) {
362 str = PyString_AsString(s);
363 if (str) {
364 e = iks_send_raw(self->parser, str);
365 if (e) {
366 Py_DECREF(s);
367 return exceptions_stream_error(e);
368 }
369 }
370 Py_DECREF(s);
371 }
372
373 Py_INCREF(Py_None);
374 return Py_None;
375 }
376
377 static PyObject *
Stream_recv(Stream * self)378 Stream_recv(Stream *self)
379 {
380 int e;
381
382 e = iks_recv(self->parser, 0);
383 if (e) {
384 return exceptions_stream_error(e);
385 }
386
387 Py_INCREF(Py_None);
388 return Py_None;
389 }
390
391 static void
Stream_dealloc(Stream * self)392 Stream_dealloc(Stream *self)
393 {
394 if (self->jid) { Py_DECREF(self->jid); }
395 iks_parser_delete(self->parser);
396 self->ob_type->tp_free((PyObject *) self);
397 }
398
399 void
Stream_setup(PyObject * module)400 Stream_setup(PyObject *module)
401 {
402 Stream_type.tp_new = PyType_GenericNew;
403 if (PyType_Ready(&Stream_type) < 0) return;
404 Py_INCREF(&Stream_type);
405
406 PyModule_AddObject(module, "Stream", (PyObject *) &Stream_type);
407 }
408