NEWS: Describe the fix for #10457
[dbus-python-phuang.git] / _dbus_bindings / message-get-args.c
blob532bd14351d3ee007fa2d1af81f184df454bd9b8
1 /* D-Bus Message unserialization. This contains all the logic to map from
2 * D-Bus types to Python objects.
4 * Copyright (C) 2006, 2007 Collabora Ltd.
6 * Licensed under the Academic Free License version 2.1
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #define PY_SIZE_T_CLEAN 1
26 #define DBG_IS_TOO_VERBOSE
27 #include "types-internal.h"
28 #include "message-internal.h"
30 char dbus_py_Message_get_args_list__doc__[] = (
31 "get_args_list(**kwargs) -> list\n\n"
32 "Return the message's arguments. Keyword arguments control the translation\n"
33 "of D-Bus types to Python:\n"
34 "\n"
35 ":Keywords:\n"
36 " `byte_arrays` : bool\n"
37 " If true, convert arrays of byte (signature 'ay') into dbus.ByteArray,\n"
38 " a str subclass. In practice, this is usually what you want, but\n"
39 " it's off by default for consistency.\n"
40 "\n"
41 " If false (default), convert them into a dbus.Array of Bytes.\n"
42 " `utf8_strings` : bool\n"
43 " If true, return D-Bus strings as Python 8-bit strings (of UTF-8).\n"
44 " If false (default), return D-Bus strings as Python unicode objects.\n"
45 "\n"
46 "Most of the type mappings should be fairly obvious:\n"
47 "\n"
48 "=============== ===================================================\n"
49 "D-Bus Python\n"
50 "=============== ===================================================\n"
51 "byte (y) dbus.Byte (int subclass)\n"
52 "bool (b) dbus.Boolean (int subclass)\n"
53 "Signature (g) dbus.Signature (str subclass)\n"
54 "intNN, uintNN dbus.IntNN, dbus.UIntNN (int or long subclasses)\n"
55 "double (d) dbus.Double\n"
56 "string (s) dbus.String (unicode subclass)\n"
57 " (or dbus.UTF8String, str subclass, if utf8_strings set)\n"
58 "Object path (o) dbus.ObjectPath (str subclass)\n"
59 "dict (a{...}) dbus.Dictionary\n"
60 "array (a...) dbus.Array (list subclass) containing appropriate types\n"
61 "byte array (ay) dbus.ByteArray (str subclass) if byte_arrays set; or\n"
62 " list of Byte\n"
63 "struct ((...)) dbus.Struct (tuple subclass) of appropriate types\n"
64 "variant (v) contained type, but with variant_level > 0\n"
65 "=============== ===================================================\n"
68 typedef struct {
69 int byte_arrays;
70 int utf8_strings;
71 } Message_get_args_options;
73 static PyObject *_message_iter_get_pyobject(DBusMessageIter *iter,
74 Message_get_args_options *opts,
75 long extra_variants);
77 /* Append all the items iterated over to the given Python list object.
78 * Return 0 on success/-1 with exception on failure. */
79 static int
80 _message_iter_append_all_to_list(DBusMessageIter *iter, PyObject *list,
81 Message_get_args_options *opts)
83 int ret, type;
84 while ((type = dbus_message_iter_get_arg_type(iter))
85 != DBUS_TYPE_INVALID) {
86 PyObject *item;
87 DBG("type == %d '%c'", type, type);
89 item = _message_iter_get_pyobject(iter, opts, 0);
90 if (!item) return -1;
91 #ifdef USING_DBG
92 fprintf(stderr, "DBG/%ld: appending to list: %p == ", (long)getpid(), item);
93 PyObject_Print(item, stderr, 0);
94 fprintf(stderr, " of type %p\n", item->ob_type);
95 #endif
96 ret = PyList_Append(list, item);
97 Py_DECREF(item);
98 item = NULL;
99 if (ret < 0) return -1;
100 #ifdef USING_DBG
101 fprintf(stderr, "DBG/%ld: list now contains: ", (long)getpid());
102 PyObject_Print(list, stderr, 0);
103 fprintf(stderr, "\n");
104 #endif
105 dbus_message_iter_next(iter);
107 return 0;
110 static inline PyObject *
111 _message_iter_get_dict(DBusMessageIter *iter,
112 Message_get_args_options *opts,
113 PyObject *kwargs)
115 DBusMessageIter entries;
116 char *sig_str = dbus_message_iter_get_signature(iter);
117 PyObject *sig;
118 PyObject *ret;
119 int status;
121 if (!sig_str) {
122 PyErr_NoMemory();
123 return NULL;
125 sig = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
126 "(s#)", sig_str+2,
127 (Py_ssize_t)strlen(sig_str)-3);
128 dbus_free(sig_str);
129 if (!sig) {
130 return NULL;
132 status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig);
133 Py_DECREF(sig);
134 if (status < 0) {
135 return NULL;
138 ret = PyObject_Call((PyObject *)&DBusPyDict_Type, dbus_py_empty_tuple, kwargs);
139 if (!ret) {
140 return NULL;
143 dbus_message_iter_recurse(iter, &entries);
144 while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) {
145 PyObject *key = NULL;
146 PyObject *value = NULL;
147 DBusMessageIter kv;
149 DBG("%s", "dict entry...");
151 dbus_message_iter_recurse(&entries, &kv);
153 key = _message_iter_get_pyobject(&kv, opts, 0);
154 if (!key) {
155 Py_DECREF(ret);
156 return NULL;
158 dbus_message_iter_next(&kv);
160 value = _message_iter_get_pyobject(&kv, opts, 0);
161 if (!value) {
162 Py_DECREF(key);
163 Py_DECREF(ret);
164 return NULL;
167 status = PyDict_SetItem(ret, key, value);
168 Py_DECREF(key);
169 Py_DECREF(value);
171 if (status < 0) {
172 Py_DECREF(ret);
173 return NULL;
175 dbus_message_iter_next(&entries);
178 return ret;
181 /* Returns a new reference. */
182 static PyObject *
183 _message_iter_get_pyobject(DBusMessageIter *iter,
184 Message_get_args_options *opts,
185 long variant_level)
187 union {
188 const char *s;
189 unsigned char y;
190 dbus_bool_t b;
191 double d;
192 float f;
193 dbus_uint16_t u16;
194 dbus_int16_t i16;
195 dbus_uint32_t u32;
196 dbus_int32_t i32;
197 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
198 dbus_uint64_t u64;
199 dbus_int64_t i64;
200 #endif
201 } u;
202 int type = dbus_message_iter_get_arg_type(iter);
203 PyObject *args = NULL;
204 PyObject *kwargs = NULL;
205 PyObject *ret = NULL;
207 /* If the variant-level is >0, prepare a dict for the kwargs.
208 * For variant wrappers optimize slightly by skipping this.
210 if (variant_level > 0 && type != DBUS_TYPE_VARIANT) {
211 PyObject *variant_level_int;
213 variant_level_int = PyInt_FromLong(variant_level);
214 if (!variant_level_int) {
215 return NULL;
217 kwargs = PyDict_New();
218 if (!kwargs) {
219 Py_DECREF(variant_level_int);
220 return NULL;
222 if (PyDict_SetItem(kwargs, dbus_py_variant_level_const,
223 variant_level_int) < 0) {
224 Py_DECREF(variant_level_int);
225 Py_DECREF(kwargs);
226 return NULL;
228 Py_DECREF(variant_level_int);
230 /* From here down you need to break from the switch to exit, so the
231 * dict is freed if necessary
234 switch (type) {
235 case DBUS_TYPE_STRING:
236 DBG("%s", "found a string");
237 dbus_message_iter_get_basic(iter, &u.s);
238 if (opts->utf8_strings) {
239 args = Py_BuildValue("(s)", u.s);
240 if (!args) break;
241 ret = PyObject_Call((PyObject *)&DBusPyUTF8String_Type,
242 args, kwargs);
244 else {
245 args = Py_BuildValue("(N)", PyUnicode_DecodeUTF8(u.s,
246 strlen(u.s),
247 NULL));
248 if (!args) {
249 break;
251 ret = PyObject_Call((PyObject *)&DBusPyString_Type,
252 args, kwargs);
254 break;
256 case DBUS_TYPE_SIGNATURE:
257 DBG("%s", "found a signature");
258 dbus_message_iter_get_basic(iter, &u.s);
259 args = Py_BuildValue("(s)", u.s);
260 if (!args) break;
261 ret = PyObject_Call((PyObject *)&DBusPySignature_Type, args, kwargs);
262 break;
264 case DBUS_TYPE_OBJECT_PATH:
265 DBG("%s", "found an object path");
266 dbus_message_iter_get_basic(iter, &u.s);
267 args = Py_BuildValue("(s)", u.s);
268 if (!args) break;
269 ret = PyObject_Call((PyObject *)&DBusPyObjectPath_Type, args, kwargs);
270 break;
272 case DBUS_TYPE_DOUBLE:
273 DBG("%s", "found a double");
274 dbus_message_iter_get_basic(iter, &u.d);
275 args = Py_BuildValue("(f)", u.d);
276 if (!args) break;
277 ret = PyObject_Call((PyObject *)&DBusPyDouble_Type, args, kwargs);
278 break;
280 #ifdef WITH_DBUS_FLOAT32
281 case DBUS_TYPE_FLOAT:
282 DBG("%s", "found a float");
283 dbus_message_iter_get_basic(iter, &u.f);
284 args = Py_BuildValue("(f)", (double)u.f);
285 if (!args) break;
286 ret = PyObject_Call((PyObject *)&DBusPyFloat_Type, args, kwargs);
287 break;
288 #endif
290 case DBUS_TYPE_INT16:
291 DBG("%s", "found an int16");
292 dbus_message_iter_get_basic(iter, &u.i16);
293 args = Py_BuildValue("(i)", (int)u.i16);
294 if (!args) break;
295 ret = PyObject_Call((PyObject *)&DBusPyInt16_Type, args, kwargs);
296 break;
298 case DBUS_TYPE_UINT16:
299 DBG("%s", "found a uint16");
300 dbus_message_iter_get_basic(iter, &u.u16);
301 args = Py_BuildValue("(i)", (int)u.u16);
302 if (!args) break;
303 ret = PyObject_Call((PyObject *)&DBusPyUInt16_Type, args, kwargs);
304 break;
306 case DBUS_TYPE_INT32:
307 DBG("%s", "found an int32");
308 dbus_message_iter_get_basic(iter, &u.i32);
309 args = Py_BuildValue("(l)", (long)u.i32);
310 if (!args) break;
311 ret = PyObject_Call((PyObject *)&DBusPyInt32_Type, args, kwargs);
312 break;
314 case DBUS_TYPE_UINT32:
315 DBG("%s", "found a uint32");
316 dbus_message_iter_get_basic(iter, &u.u32);
317 args = Py_BuildValue("(k)", (unsigned long)u.u32);
318 if (!args) break;
319 ret = PyObject_Call((PyObject *)&DBusPyUInt32_Type, args, kwargs);
320 break;
322 #if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
323 case DBUS_TYPE_INT64:
324 DBG("%s", "found an int64");
325 dbus_message_iter_get_basic(iter, &u.i64);
326 args = Py_BuildValue("(L)", (PY_LONG_LONG)u.i64);
327 if (!args) break;
328 ret = PyObject_Call((PyObject *)&DBusPyInt64_Type, args, kwargs);
329 break;
331 case DBUS_TYPE_UINT64:
332 DBG("%s", "found a uint64");
333 dbus_message_iter_get_basic(iter, &u.u64);
334 args = Py_BuildValue("(K)", (unsigned PY_LONG_LONG)u.u64);
335 if (!args) break;
336 ret = PyObject_Call((PyObject *)&DBusPyUInt64_Type, args, kwargs);
337 break;
338 #else
339 case DBUS_TYPE_INT64:
340 case DBUS_TYPE_UINT64:
341 PyErr_SetString(PyExc_NotImplementedError,
342 "64-bit integer types are not supported on "
343 "this platform");
344 break;
345 #endif
347 case DBUS_TYPE_BYTE:
348 DBG("%s", "found a byte");
349 dbus_message_iter_get_basic(iter, &u.y);
350 args = Py_BuildValue("(l)", (long)u.y);
351 if (!args)
352 break;
353 ret = PyObject_Call((PyObject *)&DBusPyByte_Type, args, kwargs);
354 break;
356 case DBUS_TYPE_BOOLEAN:
357 DBG("%s", "found a bool");
358 dbus_message_iter_get_basic(iter, &u.b);
359 args = Py_BuildValue("(l)", (long)u.b);
360 if (!args)
361 break;
362 ret = PyObject_Call((PyObject *)&DBusPyBoolean_Type, args, kwargs);
363 break;
365 case DBUS_TYPE_ARRAY:
366 DBG("%s", "found an array...");
367 /* Dicts are arrays of DBUS_TYPE_DICT_ENTRY on the wire.
368 Also, we special-case arrays of DBUS_TYPE_BYTE sometimes. */
369 type = dbus_message_iter_get_element_type(iter);
370 if (type == DBUS_TYPE_DICT_ENTRY) {
371 DBG("%s", "no, actually it's a dict...");
372 if (!kwargs) {
373 kwargs = PyDict_New();
374 if (!kwargs) break;
376 ret = _message_iter_get_dict(iter, opts, kwargs);
378 else if (opts->byte_arrays && type == DBUS_TYPE_BYTE) {
379 DBusMessageIter sub;
380 int n;
382 DBG("%s", "actually, a byte array...");
383 dbus_message_iter_recurse(iter, &sub);
384 dbus_message_iter_get_fixed_array(&sub,
385 (const unsigned char **)&u.s,
386 &n);
387 args = Py_BuildValue("(s#)", u.s, (Py_ssize_t)n);
388 if (!args) break;
389 ret = PyObject_Call((PyObject *)&DBusPyByteArray_Type,
390 args, kwargs);
392 else {
393 DBusMessageIter sub;
394 char *sig;
395 PyObject *sig_obj;
396 int status;
398 DBG("%s", "a normal array...");
399 if (!kwargs) {
400 kwargs = PyDict_New();
401 if (!kwargs) break;
403 dbus_message_iter_recurse(iter, &sub);
404 sig = dbus_message_iter_get_signature(&sub);
405 if (!sig) break;
406 sig_obj = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
407 "(s)", sig);
408 dbus_free(sig);
409 if (!sig_obj) break;
410 status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig_obj);
411 Py_DECREF(sig_obj);
412 if (status < 0) break;
413 ret = PyObject_Call((PyObject *)&DBusPyArray_Type,
414 dbus_py_empty_tuple, kwargs);
415 if (!ret) break;
416 if (_message_iter_append_all_to_list(&sub, ret, opts) < 0) {
417 Py_DECREF(ret);
418 ret = NULL;
421 break;
423 case DBUS_TYPE_STRUCT:
425 DBusMessageIter sub;
426 PyObject *list = PyList_New(0);
427 PyObject *tuple;
429 DBG("%s", "found a struct...");
430 if (!list) break;
431 dbus_message_iter_recurse(iter, &sub);
432 if (_message_iter_append_all_to_list(&sub, list, opts) < 0) {
433 Py_DECREF(list);
434 break;
436 tuple = Py_BuildValue("(O)", list);
437 if (tuple) {
438 ret = PyObject_Call((PyObject *)&DBusPyStruct_Type, tuple, kwargs);
440 else {
441 ret = NULL;
443 /* whether successful or not, we take the same action: */
444 Py_DECREF(list);
445 Py_XDECREF(tuple);
447 break;
449 case DBUS_TYPE_VARIANT:
451 DBusMessageIter sub;
453 DBG("%s", "found a variant...");
454 dbus_message_iter_recurse(iter, &sub);
455 ret = _message_iter_get_pyobject(&sub, opts, variant_level+1);
457 break;
459 default:
460 PyErr_Format(PyExc_TypeError, "Unknown type '\\%x' in D-Bus "
461 "message", type);
464 Py_XDECREF(args);
465 Py_XDECREF(kwargs);
466 return ret;
469 PyObject *
470 dbus_py_Message_get_args_list(Message *self, PyObject *args, PyObject *kwargs)
472 Message_get_args_options opts = { 0, 0 };
473 static char *argnames[] = { "byte_arrays", "utf8_strings", NULL };
474 PyObject *list;
475 DBusMessageIter iter;
477 #ifdef USING_DBG
478 fprintf(stderr, "DBG/%ld: called Message_get_args_list(self, *",
479 (long)getpid());
480 PyObject_Print(args, stderr, 0);
481 if (kwargs) {
482 fprintf(stderr, ", **");
483 PyObject_Print(kwargs, stderr, 0);
485 fprintf(stderr, ")\n");
486 #endif
488 if (PyTuple_Size(args) != 0) {
489 PyErr_SetString(PyExc_TypeError, "get_args_list takes no positional "
490 "arguments");
491 return NULL;
493 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:get_args_list",
494 argnames,
495 &(opts.byte_arrays),
496 &(opts.utf8_strings))) return NULL;
497 if (!self->msg) return DBusPy_RaiseUnusableMessage();
499 list = PyList_New(0);
500 if (!list) return NULL;
502 /* Iterate over args, if any, appending to list */
503 if (dbus_message_iter_init(self->msg, &iter)) {
504 if (_message_iter_append_all_to_list(&iter, list, &opts) < 0) {
505 Py_DECREF(list);
506 DBG_EXC("%s", "Message_get_args: appending all to list failed:");
507 return NULL;
511 #ifdef USING_DBG
512 fprintf(stderr, "DBG/%ld: message has args list ", (long)getpid());
513 PyObject_Print(list, stderr, 0);
514 fprintf(stderr, "\n");
515 #endif
517 return list;
520 /* vim:set ft=c cino< sw=4 sts=4 et: */