Actually commit the numerous copyright-statement changes.
[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.
3  *
4  * Copyright (C) 2006, 2007 Collabora Ltd.
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
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.
12  *
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.
17  *
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
21  *
22  */
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);
106     }
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;
124     }
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;
131     }
132     status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig);
133     Py_DECREF(sig);
134     if (status < 0) {
135         return NULL;
136     }
138     ret = PyObject_Call((PyObject *)&DBusPyDict_Type, dbus_py_empty_tuple, kwargs);
139     if (!ret) {
140         return NULL;
141     }
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;
157         }
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;
165         }
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;
174         }
175         dbus_message_iter_next(&entries);
176     }
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.
209      */
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;
216         }
217         kwargs = PyDict_New();
218         if (!kwargs) {
219             Py_DECREF(variant_level_int);
220             return NULL;
221         }
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;
227         }
228         Py_DECREF(variant_level_int);
229     }
230     /* From here down you need to break from the switch to exit, so the
231      * dict is freed if necessary
232      */
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);
243             }
244             else {
245                 args = Py_BuildValue("(N)", PyUnicode_DecodeUTF8(u.s,
246                                                                  strlen(u.s),
247                                                                  NULL));
248                 if (!args) {
249                     break;
250                 }
251                 ret = PyObject_Call((PyObject *)&DBusPyString_Type,
252                                     args, kwargs);
253             }
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;
375                 }
376                 ret = _message_iter_get_dict(iter, opts, kwargs);
377             }
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);
391             }
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;
402                 }
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;
419                 }
420             }
421             break;
423         case DBUS_TYPE_STRUCT:
424             {
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;
435                 }
436                 tuple = Py_BuildValue("(O)", list);
437                 if (tuple) {
438                     ret = PyObject_Call((PyObject *)&DBusPyStruct_Type, tuple, kwargs);
439                 }
440                 else {
441                     ret = NULL;
442                 }
443                 /* whether successful or not, we take the same action: */
444                 Py_DECREF(list);
445                 Py_XDECREF(tuple);
446             }
447             break;
449         case DBUS_TYPE_VARIANT:
450             {
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);
456             }
457             break;
459         default:
460             PyErr_Format(PyExc_TypeError, "Unknown type '\\%x' in D-Bus "
461                          "message", type);
462     }
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);
484     }
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;
492     }
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;
508         }
509     }
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: */