summaryrefslogtreecommitdiff
path: root/dbus_bindings/message-append.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbus_bindings/message-append.c')
-rw-r--r--dbus_bindings/message-append.c1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/dbus_bindings/message-append.c b/dbus_bindings/message-append.c
new file mode 100644
index 0000000..0480ceb
--- /dev/null
+++ b/dbus_bindings/message-append.c
@@ -0,0 +1,1294 @@
+/* D-Bus Message serialization. This contains all the logic to map from
+ * Python objects to D-Bus types.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <assert.h>
+
+#define DBG_IS_TOO_VERBOSE
+#include "compat-internal.h"
+#include "types-internal.h"
+#include "message-internal.h"
+
+/* Return the number of variants wrapping the given object. Return 0
+ * if the object is not a D-Bus type.
+ */
+static long
+get_variant_level(PyObject *obj)
+{
+ if (DBusPyString_Check(obj)) {
+ return ((DBusPyString *)obj)->variant_level;
+ }
+#ifndef PY3
+ else if (DBusPyIntBase_Check(obj)) {
+ return ((DBusPyIntBase *)obj)->variant_level;
+ }
+#endif
+ else if (DBusPyFloatBase_Check(obj)) {
+ return ((DBusPyFloatBase *)obj)->variant_level;
+ }
+ else if (DBusPyArray_Check(obj)) {
+ return ((DBusPyArray *)obj)->variant_level;
+ }
+ else if (DBusPyDict_Check(obj)) {
+ return ((DBusPyDict *)obj)->variant_level;
+ }
+ else if (DBusPyLongBase_Check(obj) ||
+#ifdef PY3
+ DBusPyBytesBase_Check(obj) ||
+#endif
+ DBusPyStrBase_Check(obj) ||
+ DBusPyStruct_Check(obj)) {
+ return dbus_py_variant_level_get(obj);
+ }
+ else {
+ return 0;
+ }
+}
+
+char dbus_py_Message_append__doc__[] = (
+"message.append(*args, **kwargs)\n"
+"\n"
+"Set the message's arguments from the positional parameter, according to\n"
+"the signature given by the ``signature`` keyword parameter.\n"
+"\n"
+"The following type conversions are supported:\n\n"
+"=============================== ===========================\n"
+"D-Bus (in signature) Python\n"
+"=============================== ===========================\n"
+"boolean (b) any object (via bool())\n"
+"byte (y) string of length 1\n"
+" any integer\n"
+"any integer type any integer\n"
+"double (d) any float\n"
+"object path anything with a __dbus_object_path__ attribute\n"
+"string, signature, object path str (must be UTF-8) or unicode\n"
+"dict (a{...}) any mapping\n"
+"array (a...) any iterable over appropriate objects\n"
+"struct ((...)) any iterable over appropriate objects\n"
+"variant any object above (guess type as below)\n"
+"=============================== ===========================\n"
+"\n"
+"Here 'any integer' means anything on which int() or long()\n"
+"(as appropriate) will work, except for basestring subclasses.\n"
+"'Any float' means anything on which float() will work, except\n"
+"for basestring subclasses.\n"
+"\n"
+"If there is no signature, guess from the arguments using\n"
+"the static method `Message.guess_signature`.\n"
+);
+
+char dbus_py_Message_guess_signature__doc__[] = (
+"guess_signature(*args) -> Signature [static method]\n\n"
+"Guess a D-Bus signature which should be used to encode the given\n"
+"Python objects.\n"
+"\n"
+"The signature is constructed as follows:\n\n"
+"+-------------------------------+---------------------------+\n"
+"|Python |D-Bus |\n"
+"+===============================+===========================+\n"
+"|D-Bus type, variant_level > 0 |variant (v) |\n"
+"+-------------------------------+---------------------------+\n"
+"|D-Bus type, variant_level == 0 |the corresponding type |\n"
+"+-------------------------------+---------------------------+\n"
+"|anything with a |object path |\n"
+"|__dbus_object_path__ attribute | |\n"
+"+-------------------------------+---------------------------+\n"
+"|bool |boolean (y) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other int subclass |int32 (i) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other long subclass |int64 (x) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other float subclass |double (d) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other str subclass |string (s) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other unicode subclass |string (s) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other tuple subclass |struct ((...)) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other list subclass |array (a...), guess |\n"
+"| |contents' type according to|\n"
+"| |type of first item |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other dict subclass |dict (a{...}), guess key, |\n"
+"| |value type according to |\n"
+"| |types for an arbitrary item|\n"
+"+-------------------------------+---------------------------+\n"
+"|anything else |raise TypeError |\n"
+"+-------------------------------+---------------------------+\n"
+);
+
+/* return a new reference, possibly to None */
+static PyObject *
+get_object_path(PyObject *obj)
+{
+ PyObject *magic_attr = PyObject_GetAttr(obj, dbus_py__dbus_object_path__const);
+
+ if (magic_attr) {
+ if (PyUnicode_Check(magic_attr) || PyBytes_Check(magic_attr)) {
+ return magic_attr;
+ }
+ else {
+ Py_CLEAR(magic_attr);
+ PyErr_SetString(PyExc_TypeError, "__dbus_object_path__ must be "
+ "a string");
+ return NULL;
+ }
+ }
+ else {
+ /* Ignore exceptions, except for SystemExit and KeyboardInterrupt */
+ if (PyErr_ExceptionMatches(PyExc_SystemExit) ||
+ PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
+ return NULL;
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+}
+
+/* Return a new reference. If the object is a variant and variant_level_ptr
+ * is not NULL, put the variant level in the variable pointed to, and
+ * return the contained type instead of "v". */
+static PyObject *
+_signature_string_from_pyobject(PyObject *obj, long *variant_level_ptr)
+{
+ PyObject *magic_attr;
+ long variant_level = get_variant_level(obj);
+
+ if (variant_level < 0)
+ return NULL;
+
+ if (variant_level_ptr) {
+ *variant_level_ptr = variant_level;
+ }
+ else if (variant_level > 0) {
+ return NATIVESTR_FROMSTR(DBUS_TYPE_VARIANT_AS_STRING);
+ }
+
+ if (obj == Py_True || obj == Py_False) {
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ }
+
+ magic_attr = get_object_path(obj);
+ if (!magic_attr)
+ return NULL;
+ if (magic_attr != Py_None) {
+ Py_CLEAR(magic_attr);
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ }
+ Py_CLEAR(magic_attr);
+
+ /* Ordering is important: some of these are subclasses of each other. */
+#ifdef PY3
+ if (PyLong_Check(obj)) {
+ if (DBusPyUInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT64_AS_STRING);
+ else if (DBusPyInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ else if (DBusPyUInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT32_AS_STRING);
+ else if (DBusPyInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ else if (DBusPyUInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT16_AS_STRING);
+ else if (DBusPyInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT16_AS_STRING);
+ else if (DBusPyByte_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BYTE_AS_STRING);
+ else if (DBusPyBoolean_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ }
+#else /* !PY3 */
+ if (PyInt_Check(obj)) {
+ if (DBusPyInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT16_AS_STRING);
+ else if (DBusPyInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ else if (DBusPyByte_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BYTE_AS_STRING);
+ else if (DBusPyUInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT16_AS_STRING);
+ else if (DBusPyBoolean_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ }
+ else if (PyLong_Check(obj)) {
+ if (DBusPyInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ else if (DBusPyUInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT32_AS_STRING);
+ else if (DBusPyUInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT64_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ }
+#endif /* PY3 */
+ else if (PyUnicode_Check(obj)) {
+ /* Object paths and signatures are unicode subtypes in Python 3
+ * (the first two cases will never be true in Python 2) */
+ if (DBusPyObjectPath_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ else if (DBusPySignature_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_SIGNATURE_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_STRING_AS_STRING);
+ }
+#if defined(DBUS_TYPE_UNIX_FD)
+ else if (DBusPyUnixFd_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UNIX_FD_AS_STRING);
+#endif
+ else if (PyFloat_Check(obj)) {
+#ifdef WITH_DBUS_FLOAT32
+ if (DBusPyDouble_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_DOUBLE_AS_STRING);
+ else if (DBusPyFloat_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_FLOAT_AS_STRING);
+ else
+#endif
+ return NATIVESTR_FROMSTR(DBUS_TYPE_DOUBLE_AS_STRING);
+ }
+ else if (PyBytes_Check(obj)) {
+ /* Object paths and signatures are bytes subtypes in Python 2
+ * (the first two cases will never be true in Python 3) */
+ if (DBusPyObjectPath_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ else if (DBusPySignature_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_SIGNATURE_AS_STRING);
+ else if (DBusPyByteArray_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_STRING_AS_STRING);
+ }
+ else if (PyTuple_Check(obj)) {
+ Py_ssize_t len = PyTuple_GET_SIZE(obj);
+ PyObject *list = PyList_New(len + 2); /* new ref */
+ PyObject *item; /* temporary new ref */
+ PyObject *empty_str; /* temporary new ref */
+ PyObject *ret;
+ Py_ssize_t i;
+
+ if (!list) return NULL;
+ if (len == 0) {
+ PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty");
+ Py_CLEAR(list);
+ return NULL;
+ }
+ /* Set the first and last elements of list to be the parentheses */
+ item = NATIVESTR_FROMSTR(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
+ if (PyList_SetItem(list, 0, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NATIVESTR_FROMSTR(DBUS_STRUCT_END_CHAR_AS_STRING);
+ if (PyList_SetItem(list, len + 1, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ if (!item || !PyList_GET_ITEM(list, 0)) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NULL;
+
+ for (i = 0; i < len; i++) {
+ item = PyTuple_GetItem(obj, i);
+ if (!item) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = _signature_string_from_pyobject(item, NULL);
+ if (!item) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ if (PyList_SetItem(list, i + 1, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NULL;
+ }
+ empty_str = NATIVESTR_FROMSTR("");
+ if (!empty_str) {
+ /* really shouldn't happen */
+ Py_CLEAR(list);
+ return NULL;
+ }
+ ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */
+ /* whether ret is NULL or not, */
+ Py_CLEAR(empty_str);
+ Py_CLEAR(list);
+ return ret;
+ }
+ else if (PyList_Check(obj)) {
+ PyObject *tmp;
+ PyObject *ret = NATIVESTR_FROMSTR(DBUS_TYPE_ARRAY_AS_STRING);
+ if (!ret) return NULL;
+#ifdef PY3
+ if (DBusPyArray_Check(obj) &&
+ PyUnicode_Check(((DBusPyArray *)obj)->signature))
+ {
+ PyObject *concat = PyUnicode_Concat(
+ ret, ((DBusPyArray *)obj)->signature);
+ Py_CLEAR(ret);
+ return concat;
+ }
+#else
+ if (DBusPyArray_Check(obj) &&
+ PyBytes_Check(((DBusPyArray *)obj)->signature))
+ {
+ PyBytes_Concat(&ret, ((DBusPyArray *)obj)->signature);
+ return ret;
+ }
+#endif
+ if (PyList_GET_SIZE(obj) == 0) {
+ /* No items, so fail. Or should we guess "av"? */
+ PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
+ "from an empty list");
+ return NULL;
+ }
+ tmp = PyList_GetItem(obj, 0);
+ tmp = _signature_string_from_pyobject(tmp, NULL);
+ if (!tmp) return NULL;
+#ifdef PY3
+ {
+ PyObject *concat = PyUnicode_Concat(ret, tmp);
+ Py_CLEAR(ret);
+ Py_CLEAR(tmp);
+ return concat;
+ }
+#else
+ PyBytes_ConcatAndDel(&ret, tmp);
+ return ret;
+#endif
+ }
+ else if (PyDict_Check(obj)) {
+ PyObject *key, *value, *keysig, *valuesig;
+ Py_ssize_t pos = 0;
+ PyObject *ret = NULL;
+
+#ifdef PY3
+ if (DBusPyDict_Check(obj) &&
+ PyUnicode_Check(((DBusPyDict *)obj)->signature))
+ {
+ return PyUnicode_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%U"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ ((DBusPyDict *)obj)->signature);
+ }
+#else
+ if (DBusPyDict_Check(obj) &&
+ PyBytes_Check(((DBusPyDict *)obj)->signature))
+ {
+ const char *sig = PyBytes_AS_STRING(((DBusPyDict *)obj)->signature);
+
+ return PyBytes_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%s"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ sig);
+ }
+#endif
+ if (!PyDict_Next(obj, &pos, &key, &value)) {
+ /* No items, so fail. Or should we guess "a{vv}"? */
+ PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
+ "from an empty dict");
+ return NULL;
+ }
+ keysig = _signature_string_from_pyobject(key, NULL);
+ valuesig = _signature_string_from_pyobject(value, NULL);
+ if (keysig && valuesig) {
+#ifdef PY3
+ ret = PyUnicode_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%U%U"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ keysig, valuesig);
+#else
+ ret = PyBytes_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%s%s"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ PyBytes_AS_STRING(keysig),
+ PyBytes_AS_STRING(valuesig));
+#endif
+ }
+ Py_CLEAR(keysig);
+ Py_CLEAR(valuesig);
+ return ret;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError, "Don't know which D-Bus type "
+ "to use to encode type \"%s\"",
+ Py_TYPE(obj)->tp_name);
+ return NULL;
+ }
+}
+
+PyObject *
+dbus_py_Message_guess_signature(PyObject *unused UNUSED, PyObject *args)
+{
+ PyObject *tmp, *ret = NULL;
+
+ if (!args) {
+ if (!PyErr_Occurred()) {
+ PyErr_BadInternalCall();
+ }
+ return NULL;
+ }
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: called Message_guess_signature", (long)getpid());
+ PyObject_Print(args, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ if (!PyTuple_Check(args)) {
+ DBG("%s", "Message_guess_signature: args not a tuple");
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ /* if there were no args, easy */
+ if (PyTuple_GET_SIZE(args) == 0) {
+ DBG("%s", "Message_guess_signature: no args, so return Signature('')");
+ return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", "");
+ }
+
+ /* if there were args, the signature we want is, by construction,
+ * exactly the signature we get for the tuple args, except that we don't
+ * want the parentheses. */
+ tmp = _signature_string_from_pyobject(args, NULL);
+ if (!tmp) {
+ DBG("%s", "Message_guess_signature: failed");
+ return NULL;
+ }
+ if (PyUnicode_Check(tmp)) {
+ PyObject *as_bytes = PyUnicode_AsUTF8String(tmp);
+ Py_CLEAR(tmp);
+ if (!as_bytes)
+ return NULL;
+ if (PyBytes_GET_SIZE(as_bytes) < 2) {
+ PyErr_SetString(PyExc_RuntimeError, "Internal error: "
+ "_signature_string_from_pyobject returned "
+ "a bad result");
+ Py_CLEAR(as_bytes);
+ return NULL;
+ }
+ tmp = as_bytes;
+ }
+ if (!PyBytes_Check(tmp) || PyBytes_GET_SIZE(tmp) < 2) {
+ PyErr_SetString(PyExc_RuntimeError, "Internal error: "
+ "_signature_string_from_pyobject returned "
+ "a bad result");
+ Py_CLEAR(tmp);
+ return NULL;
+ }
+ ret = PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s#)",
+ PyBytes_AS_STRING(tmp) + 1,
+ PyBytes_GET_SIZE(tmp) - 2);
+ Py_CLEAR(tmp);
+ return ret;
+}
+
+static int _message_iter_append_pyobject(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *obj,
+ dbus_bool_t *more);
+static int _message_iter_append_variant(DBusMessageIter *appender,
+ PyObject *obj);
+
+static int
+_message_iter_append_string(DBusMessageIter *appender,
+ int sig_type, PyObject *obj,
+ dbus_bool_t allow_object_path_attr)
+{
+ char *s;
+ PyObject *utf8;
+
+ if (sig_type == DBUS_TYPE_OBJECT_PATH && allow_object_path_attr) {
+ PyObject *object_path = get_object_path (obj);
+
+ if (object_path == Py_None) {
+ Py_CLEAR(object_path);
+ }
+ else if (!object_path) {
+ return -1;
+ }
+ else {
+ int ret = _message_iter_append_string(appender, sig_type,
+ object_path, FALSE);
+ Py_CLEAR(object_path);
+ return ret;
+ }
+ }
+
+ if (PyBytes_Check(obj)) {
+ utf8 = obj;
+ Py_INCREF(obj);
+ }
+ else if (PyUnicode_Check(obj)) {
+ utf8 = PyUnicode_AsUTF8String(obj);
+ if (!utf8) return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "Expected a string or unicode object");
+ return -1;
+ }
+
+ /* Raise TypeError if the string has embedded NULs */
+ if (PyBytes_AsStringAndSize(utf8, &s, NULL) < 0)
+ return -1;
+
+ /* Validate UTF-8, strictly */
+ if (!dbus_validate_utf8(s, NULL)) {
+ PyErr_SetString(PyExc_UnicodeError, "String parameters "
+ "to be sent over D-Bus must be valid UTF-8 "
+ "with no noncharacter code points");
+ return -1;
+ }
+
+ DBG("Performing actual append: string (from unicode) %s", s);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &s)) {
+ Py_CLEAR(utf8);
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ Py_CLEAR(utf8);
+ return 0;
+}
+
+static int
+_message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
+{
+ unsigned char y;
+
+ if (PyBytes_Check(obj)) {
+ if (PyBytes_GET_SIZE(obj) != 1) {
+ PyErr_Format(PyExc_ValueError,
+ "Expected a length-1 bytes but found %d bytes",
+ (int)PyBytes_GET_SIZE(obj));
+ return -1;
+ }
+ y = *(unsigned char *)PyBytes_AS_STRING(obj);
+ }
+ else {
+ /* on Python 2 this accepts either int or long */
+ long i = PyLong_AsLong(obj);
+
+ if (i == -1 && PyErr_Occurred()) return -1;
+ if (i < 0 || i > 0xff) {
+ PyErr_Format(PyExc_ValueError,
+ "%d outside range for a byte value",
+ (int)i);
+ return -1;
+ }
+ y = i;
+ }
+ DBG("Performing actual append: byte \\x%02x", (unsigned)y);
+ if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+static dbus_bool_t
+dbuspy_message_iter_close_container(DBusMessageIter *iter,
+ DBusMessageIter *sub,
+ dbus_bool_t is_ok)
+{
+ if (!is_ok) {
+ dbus_message_iter_abandon_container(iter, sub);
+ return TRUE;
+ }
+ return dbus_message_iter_close_container(iter, sub);
+}
+
+#if defined(DBUS_TYPE_UNIX_FD)
+static int
+_message_iter_append_unixfd(DBusMessageIter *appender, PyObject *obj)
+{
+ int fd;
+ long original_fd;
+
+ if (INTORLONG_CHECK(obj))
+ {
+ /* on Python 2 this accepts either int or long */
+ original_fd = PyLong_AsLong(obj);
+ if (original_fd == -1 && PyErr_Occurred())
+ return -1;
+ if (original_fd < INT_MIN || original_fd > INT_MAX) {
+ PyErr_Format(PyExc_ValueError, "out of int range: %ld",
+ original_fd);
+ return -1;
+ }
+ fd = (int)original_fd;
+ }
+ else if (PyObject_IsInstance(obj, (PyObject*) &DBusPyUnixFd_Type)) {
+ fd = dbus_py_unix_fd_get_fd(obj);
+ }
+ else {
+ return -1;
+ }
+
+ DBG("Performing actual append: fd %d", fd);
+ if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_UNIX_FD, &fd)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static int
+_message_iter_append_dictentry(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *dict, PyObject *key)
+{
+ DBusSignatureIter sub_sig_iter;
+ DBusMessageIter sub;
+ int ret = -1;
+ PyObject *value = PyObject_GetItem(dict, key);
+ dbus_bool_t more;
+
+ if (!value) return -1;
+
+#ifdef USING_DBG
+ fprintf(stderr, "Append dictentry: ");
+ PyObject_Print(key, stderr, 0);
+ fprintf(stderr, " => ");
+ PyObject_Print(value, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+
+ DBG("%s", "Opening DICT_ENTRY container");
+ if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
+ NULL, &sub)) {
+ PyErr_NoMemory();
+ goto out;
+ }
+ ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key, &more);
+ if (ret == 0) {
+ ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value, &more);
+ }
+ DBG("%s", "Closing DICT_ENTRY container");
+ if (!dbuspy_message_iter_close_container(appender, &sub, (ret == 0))) {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+out:
+ Py_CLEAR(value);
+ return ret;
+}
+
+static int
+_message_iter_append_multi(DBusMessageIter *appender,
+ const DBusSignatureIter *sig_iter,
+ int mode, PyObject *obj)
+{
+ DBusMessageIter sub_appender;
+ DBusSignatureIter sub_sig_iter;
+ PyObject *contents;
+ int ret;
+ PyObject *iterator = PyObject_GetIter(obj);
+ char *sig = NULL;
+ int container = mode;
+ dbus_bool_t is_byte_array = DBusPyByteArray_Check(obj);
+ int inner_type;
+ dbus_bool_t more;
+
+ assert(mode == DBUS_TYPE_DICT_ENTRY || mode == DBUS_TYPE_ARRAY ||
+ mode == DBUS_TYPE_STRUCT);
+
+#ifdef USING_DBG
+ fprintf(stderr, "Appending multiple: ");
+ PyObject_Print(obj, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ if (!iterator) return -1;
+ if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
+
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+ inner_type = dbus_signature_iter_get_current_type(&sub_sig_iter);
+
+ if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
+ sig = dbus_signature_iter_get_signature(&sub_sig_iter);
+ if (!sig) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+ /* else leave sig set to NULL. */
+
+ DBG("Opening '%c' container", container);
+ if (!dbus_message_iter_open_container(appender, container,
+ sig, &sub_appender)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ ret = 0;
+ more = TRUE;
+ while ((contents = PyIter_Next(iterator))) {
+
+ if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+ }
+ else /* struct */ {
+ if (!more) {
+ PyErr_Format(PyExc_TypeError, "Fewer items found in struct's "
+ "D-Bus signature than in Python arguments ");
+ ret = -1;
+ break;
+ }
+ }
+
+ if (mode == DBUS_TYPE_DICT_ENTRY) {
+ ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
+ obj, contents);
+ }
+ else if (mode == DBUS_TYPE_ARRAY && is_byte_array
+ && inner_type == DBUS_TYPE_VARIANT) {
+ /* Subscripting a ByteArray gives a str of length 1, but if the
+ * container is a ByteArray and the parameter is an array of
+ * variants, we want to produce an array of variants containing
+ * bytes, not strings.
+ */
+ PyObject *args = Py_BuildValue("(O)", contents);
+ PyObject *byte;
+
+ if (!args)
+ break;
+ byte = PyObject_Call((PyObject *)&DBusPyByte_Type, args, NULL);
+ Py_CLEAR(args);
+ if (!byte)
+ break;
+ ret = _message_iter_append_variant(&sub_appender, byte);
+ Py_CLEAR(byte);
+ }
+ else {
+ /* advances sub_sig_iter and sets more on success - for array
+ * this doesn't matter, for struct it's essential */
+ ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
+ contents, &more);
+ }
+
+ Py_CLEAR(contents);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ if (PyErr_Occurred()) {
+ ret = -1;
+ }
+ else if (mode == DBUS_TYPE_STRUCT && more) {
+ PyErr_Format(PyExc_TypeError, "More items found in struct's D-Bus "
+ "signature than in Python arguments ");
+ ret = -1;
+ }
+
+ /* This must be run as cleanup, even on failure. */
+ DBG("Closing '%c' container", container);
+ if (!dbuspy_message_iter_close_container(appender, &sub_appender, (ret == 0))) {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+
+out:
+ Py_CLEAR(iterator);
+ dbus_free(sig);
+ return ret;
+}
+
+static int
+_message_iter_append_string_as_byte_array(DBusMessageIter *appender,
+ PyObject *obj)
+{
+ /* a bit of a faster path for byte arrays that are strings */
+ Py_ssize_t len = PyBytes_GET_SIZE(obj);
+ const char *s;
+ DBusMessageIter sub;
+ int ret;
+
+ s = PyBytes_AS_STRING(obj);
+ DBG("%s", "Opening ARRAY container");
+ if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &sub)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ DBG("Appending fixed array of %d bytes", (int)len);
+ if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
+ ret = 0;
+ }
+ else {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+ DBG("%s", "Closing ARRAY container");
+ if (!dbus_message_iter_close_container(appender, &sub)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return ret;
+}
+
+/* Encode some Python object into a D-Bus variant slot. */
+static int
+_message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
+{
+ DBusSignatureIter obj_sig_iter;
+ const char *obj_sig_str;
+ PyObject *obj_sig;
+ int ret;
+ long variant_level;
+ dbus_bool_t dummy;
+ DBusMessageIter *variant_iters = NULL;
+
+ /* Separate the object into the contained object, and the number of
+ * variants it's wrapped in. */
+ obj_sig = _signature_string_from_pyobject(obj, &variant_level);
+ if (!obj_sig) return -1;
+
+ if (PyUnicode_Check(obj_sig)) {
+ PyObject *obj_sig_as_bytes = PyUnicode_AsUTF8String(obj_sig);
+ Py_CLEAR(obj_sig);
+ if (!obj_sig_as_bytes)
+ return -1;
+ obj_sig = obj_sig_as_bytes;
+ }
+ obj_sig_str = PyBytes_AsString(obj_sig);
+ if (!obj_sig_str) {
+ Py_CLEAR(obj_sig);
+ return -1;
+ }
+
+ if (variant_level < 1) {
+ variant_level = 1;
+ }
+
+ dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
+
+ {
+ long i;
+
+ variant_iters = calloc (variant_level, sizeof (DBusMessageIter));
+
+ if (!variant_iters) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < variant_level; i++) {
+ DBusMessageIter *child = &variant_iters[i];
+ /* The first is a special case: its parent is the iter passed in
+ * to this function, instead of being the previous one in the
+ * stack
+ */
+ DBusMessageIter *parent = (i == 0
+ ? appender
+ : &(variant_iters[i-1]));
+ /* The last is also a special case: it contains the actual
+ * object, rather than another variant
+ */
+ const char *sig_str = (i == variant_level-1
+ ? obj_sig_str
+ : DBUS_TYPE_VARIANT_AS_STRING);
+
+ DBG("Opening VARIANT container %p inside %p containing '%s'",
+ child, parent, sig_str);
+ if (!dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
+ sig_str, child)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* Put the object itself into the innermost variant */
+ ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
+ &obj_sig_iter, obj, &dummy);
+
+ /* here we rely on i (and variant_level) being a signed long */
+ for (i = variant_level - 1; i >= 0; i--) {
+ DBusMessageIter *child = &variant_iters[i];
+ /* The first is a special case: its parent is the iter passed in
+ * to this function, instead of being the previous one in the
+ * stack
+ */
+ DBusMessageIter *parent = (i == 0 ? appender
+ : &(variant_iters[i-1]));
+
+ DBG("Closing VARIANT container %p inside %p", child, parent);
+ if (!dbus_message_iter_close_container(parent, child)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (variant_iters != NULL)
+ free (variant_iters);
+
+ Py_CLEAR(obj_sig);
+ return ret;
+}
+
+/* On success, *more is set to whether there's more in the signature. */
+static int
+_message_iter_append_pyobject(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *obj,
+ dbus_bool_t *more)
+{
+ int sig_type = dbus_signature_iter_get_current_type(sig_iter);
+ DBusBasicValue u;
+ int ret = -1;
+
+#ifdef USING_DBG
+ fprintf(stderr, "Appending object at %p: ", obj);
+ PyObject_Print(obj, stderr, 0);
+ fprintf(stderr, " into appender at %p, dbus wants type %c\n",
+ appender, sig_type);
+#endif
+
+ switch (sig_type) {
+ /* The numeric types are relatively simple to deal with, so are
+ * inlined here. */
+
+ case DBUS_TYPE_BOOLEAN:
+ if (PyObject_IsTrue(obj)) {
+ u.bool_val = 1;
+ }
+ else {
+ u.bool_val = 0;
+ }
+ DBG("Performing actual append: bool(%ld)", (long)u.bool_val);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.bool_val)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ u.dbl = PyFloat_AsDouble(obj);
+ if (PyErr_Occurred()) {
+ ret = -1;
+ break;
+ }
+ DBG("Performing actual append: double(%f)", u.dbl);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.dbl)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+
+#ifdef WITH_DBUS_FLOAT32
+ case DBUS_TYPE_FLOAT:
+ u.dbl = PyFloat_AsDouble(obj);
+ if (PyErr_Occurred()) {
+ ret = -1;
+ break;
+ }
+ /* FIXME: DBusBasicValue will need to grow a float member if
+ * float32 becomes supported */
+ u.f = (float)u.dbl;
+ DBG("Performing actual append: float(%f)", u.f);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+#endif
+
+ /* The integer types are all basically the same - we delegate to
+ intNN_range_check() */
+#define PROCESS_INTEGER(size, member) \
+ u.member = dbus_py_##size##_range_check(obj);\
+ if (u.member == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
+ ret = -1; \
+ break; \
+ }\
+ DBG("Performing actual append: " #size "(%lld)", (long long)u.member); \
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.member)) {\
+ PyErr_NoMemory();\
+ ret = -1;\
+ break;\
+ } \
+ ret = 0;
+
+ case DBUS_TYPE_INT16:
+ PROCESS_INTEGER(int16, i16)
+ break;
+ case DBUS_TYPE_UINT16:
+ PROCESS_INTEGER(uint16, u16)
+ break;
+ case DBUS_TYPE_INT32:
+ PROCESS_INTEGER(int32, i32)
+ break;
+ case DBUS_TYPE_UINT32:
+ PROCESS_INTEGER(uint32, u32)
+ break;
+#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
+ case DBUS_TYPE_INT64:
+ PROCESS_INTEGER(int64, i64)
+ break;
+ case DBUS_TYPE_UINT64:
+ PROCESS_INTEGER(uint64, u64)
+ break;
+#else
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
+ "types are not supported on this platform");
+ ret = -1;
+ break;
+#endif
+#undef PROCESS_INTEGER
+
+ /* Now the more complicated cases, which are delegated to helper
+ * functions (although in practice, the compiler will hopefully
+ * inline them anyway). */
+
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_SIGNATURE:
+ case DBUS_TYPE_OBJECT_PATH:
+ ret = _message_iter_append_string(appender, sig_type, obj, TRUE);
+ break;
+
+ case DBUS_TYPE_BYTE:
+ ret = _message_iter_append_byte(appender, obj);
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ /* 3 cases - it might actually be a dict, or it might be a byte array
+ * being copied from a string (for which we have a faster path),
+ * or it might be a generic array. */
+
+ sig_type = dbus_signature_iter_get_element_type(sig_iter);
+ if (sig_type == DBUS_TYPE_DICT_ENTRY)
+ ret = _message_iter_append_multi(appender, sig_iter,
+ DBUS_TYPE_DICT_ENTRY, obj);
+ else if (sig_type == DBUS_TYPE_BYTE && PyBytes_Check(obj))
+ ret = _message_iter_append_string_as_byte_array(appender, obj);
+ else
+ ret = _message_iter_append_multi(appender, sig_iter,
+ DBUS_TYPE_ARRAY, obj);
+ DBG("_message_iter_append_multi(): %d", ret);
+ break;
+
+ case DBUS_TYPE_STRUCT:
+ ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ ret = _message_iter_append_variant(appender, obj);
+ break;
+
+ case DBUS_TYPE_INVALID:
+ PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
+ "signature than in Python arguments");
+ ret = -1;
+ break;
+
+#if defined(DBUS_TYPE_UNIX_FD)
+ case DBUS_TYPE_UNIX_FD:
+ ret = _message_iter_append_unixfd(appender, obj);
+ break;
+#endif
+
+ default:
+ PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
+ "signature", sig_type);
+ ret = -1;
+ break;
+ }
+ if (ret < 0) return -1;
+
+ DBG("Advancing signature iter at %p", sig_iter);
+ *more = dbus_signature_iter_next(sig_iter);
+#ifdef USING_DBG
+ DBG("- result: %ld, type %02x '%c'", (long)(*more),
+ (int)dbus_signature_iter_get_current_type(sig_iter),
+ (int)dbus_signature_iter_get_current_type(sig_iter));
+#endif
+ return 0;
+}
+
+
+PyObject *
+dbus_py_Message_append(Message *self, PyObject *args, PyObject *kwargs)
+{
+ const char *signature = NULL;
+ PyObject *signature_obj = NULL;
+ DBusSignatureIter sig_iter;
+ DBusMessageIter appender;
+ static char *argnames[] = {"signature", NULL};
+ dbus_bool_t more;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
+ PyObject_Print(args, stderr, 0);
+ if (kwargs) {
+ fprintf(stderr, ", **");
+ PyObject_Print(kwargs, stderr, 0);
+ }
+ fprintf(stderr, ")\n");
+#endif
+
+ /* only use kwargs for this step: deliberately ignore args for now */
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs, "|z:append",
+ argnames, &signature)) return NULL;
+
+ if (!signature) {
+ DBG("%s", "No signature for message, guessing...");
+ signature_obj = dbus_py_Message_guess_signature(NULL, args);
+ if (!signature_obj) return NULL;
+ if (PyUnicode_Check(signature_obj)) {
+ PyObject *signature_as_bytes;
+ signature_as_bytes = PyUnicode_AsUTF8String(signature_obj);
+ Py_CLEAR(signature_obj);
+ if (!signature_as_bytes)
+ return NULL;
+ signature_obj = signature_as_bytes;
+ }
+ else {
+ assert(PyBytes_Check(signature_obj));
+ }
+ signature = PyBytes_AS_STRING(signature_obj);
+ }
+ /* from here onwards, you have to do a goto rather than returning NULL
+ to make sure signature_obj gets freed */
+
+ /* iterate over args and the signature, together */
+ if (!dbus_signature_validate(signature, NULL)) {
+ PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
+ goto err;
+ }
+ dbus_message_iter_init_append(self->msg, &appender);
+
+ if (signature[0] != '\0') {
+ int i = 0;
+
+ more = TRUE;
+ dbus_signature_iter_init(&sig_iter, signature);
+ while (more) {
+ if (i >= PyTuple_GET_SIZE(args)) {
+ PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
+ "signature than in Python arguments");
+ goto hosed;
+ }
+ if (_message_iter_append_pyobject(&appender, &sig_iter,
+ PyTuple_GET_ITEM(args, i),
+ &more) < 0) {
+ goto hosed;
+ }
+ i++;
+ }
+ if (i < PyTuple_GET_SIZE(args)) {
+ PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
+ "signature than in Python arguments");
+ goto hosed;
+ }
+ }
+
+ /* success! */
+ Py_CLEAR(signature_obj);
+ Py_RETURN_NONE;
+
+hosed:
+ /* "If appending any of the arguments fails due to lack of memory,
+ * generally the message is hosed and you have to start over" -libdbus docs
+ * Enforce this by throwing away the message structure.
+ */
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+err:
+ Py_CLEAR(signature_obj);
+ return NULL;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */