Blame _dbus_bindings/conn-methods.c

Packit 130fc8
/* Implementation of normal Python-accessible methods on the _dbus_bindings
Packit 130fc8
 * Connection type; separated out to keep the file size manageable.
Packit 130fc8
 *
Packit 130fc8
 * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
Packit 130fc8
 *
Packit 130fc8
 * Permission is hereby granted, free of charge, to any person
Packit 130fc8
 * obtaining a copy of this software and associated documentation
Packit 130fc8
 * files (the "Software"), to deal in the Software without
Packit 130fc8
 * restriction, including without limitation the rights to use, copy,
Packit 130fc8
 * modify, merge, publish, distribute, sublicense, and/or sell copies
Packit 130fc8
 * of the Software, and to permit persons to whom the Software is
Packit 130fc8
 * furnished to do so, subject to the following conditions:
Packit 130fc8
 *
Packit 130fc8
 * The above copyright notice and this permission notice shall be
Packit 130fc8
 * included in all copies or substantial portions of the Software.
Packit 130fc8
 *
Packit 130fc8
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 130fc8
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 130fc8
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 130fc8
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
Packit 130fc8
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
Packit 130fc8
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Packit 130fc8
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Packit 130fc8
 * DEALINGS IN THE SOFTWARE.
Packit 130fc8
 */
Packit 130fc8
Packit 130fc8
#include "dbus_bindings-internal.h"
Packit 130fc8
Packit 130fc8
#include "conn-internal.h"
Packit 130fc8
Packit 130fc8
static void
Packit 130fc8
_object_path_unregister(DBusConnection *conn, void *user_data)
Packit 130fc8
{
Packit 130fc8
    PyGILState_STATE gil = PyGILState_Ensure();
Packit 130fc8
    PyObject *tuple = NULL;
Packit 130fc8
    Connection *conn_obj = NULL;
Packit 130fc8
    PyObject *callable;
Packit 130fc8
Packit 130fc8
    conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
Packit 130fc8
    if (!conn_obj) goto out;
Packit 130fc8
    TRACE(conn_obj);
Packit 130fc8
Packit 130fc8
    DBG("Connection at %p unregistering object path %s",
Packit 130fc8
        conn_obj, PyBytes_AS_STRING((PyObject *)user_data));
Packit 130fc8
    tuple = DBusPyConnection_GetObjectPathHandlers(
Packit 130fc8
        (PyObject *)conn_obj, (PyObject *)user_data);
Packit 130fc8
    if (!tuple) goto out;
Packit 130fc8
    if (tuple == Py_None) goto out;
Packit 130fc8
Packit 130fc8
    DBG("%s", "... yes we have handlers for that object path");
Packit 130fc8
Packit 130fc8
    /* 0'th item is the unregisterer (if that's a word) */
Packit 130fc8
    callable = PyTuple_GetItem(tuple, 0);
Packit 130fc8
    if (callable && callable != Py_None) {
Packit 130fc8
        DBG("%s", "... and we even have an unregisterer");
Packit 130fc8
        /* any return from the unregisterer is ignored */
Packit 130fc8
        Py_XDECREF(PyObject_CallFunctionObjArgs(callable, conn_obj, NULL));
Packit 130fc8
    }
Packit 130fc8
out:
Packit 130fc8
    Py_CLEAR(conn_obj);
Packit 130fc8
    Py_CLEAR(tuple);
Packit 130fc8
    /* the user_data (a Python str) is no longer ref'd by the DBusConnection */
Packit 130fc8
    Py_CLEAR(user_data);
Packit 130fc8
    if (PyErr_Occurred()) {
Packit 130fc8
        PyErr_Print();
Packit 130fc8
    }
Packit 130fc8
    PyGILState_Release(gil);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
static DBusHandlerResult
Packit 130fc8
_object_path_message(DBusConnection *conn, DBusMessage *message,
Packit 130fc8
                     void *user_data)
Packit 130fc8
{
Packit 130fc8
    DBusHandlerResult ret;
Packit 130fc8
    PyGILState_STATE gil = PyGILState_Ensure();
Packit 130fc8
    Connection *conn_obj = NULL;
Packit 130fc8
    PyObject *tuple = NULL;
Packit 130fc8
    PyObject *msg_obj;
Packit 130fc8
    PyObject *callable;             /* borrowed */
Packit 130fc8
Packit 130fc8
    dbus_message_ref(message);
Packit 130fc8
    msg_obj = DBusPyMessage_ConsumeDBusMessage(message);
Packit 130fc8
    if (!msg_obj) {
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
Packit 130fc8
    if (!conn_obj) {
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
    TRACE(conn_obj);
Packit 130fc8
Packit 130fc8
    DBG("Connection at %p messaging object path %s",
Packit 130fc8
        conn_obj, PyBytes_AS_STRING((PyObject *)user_data));
Packit 130fc8
    DBG_DUMP_MESSAGE(message);
Packit 130fc8
    tuple = DBusPyConnection_GetObjectPathHandlers(
Packit 130fc8
        (PyObject *)conn_obj, (PyObject *)user_data);
Packit 130fc8
    if (!tuple || tuple == Py_None) {
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    DBG("%s", "... yes we have handlers for that object path");
Packit 130fc8
Packit 130fc8
    /* 1st item (0-based) is the message callback */
Packit 130fc8
    callable = PyTuple_GetItem(tuple, 1);
Packit 130fc8
    if (!callable) {
Packit 130fc8
        DBG("%s", "... error getting message handler from tuple");
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
    }
Packit 130fc8
    else if (callable == Py_None) {
Packit 130fc8
        /* there was actually no handler after all */
Packit 130fc8
        DBG("%s", "... but those handlers don't do messages");
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        DBG("%s", "... and we have a message handler for that object path");
Packit 130fc8
        ret = DBusPyConnection_HandleMessage(conn_obj, msg_obj, callable);
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
out:
Packit 130fc8
    Py_CLEAR(msg_obj);
Packit 130fc8
    Py_CLEAR(conn_obj);
Packit 130fc8
    Py_CLEAR(tuple);
Packit 130fc8
    if (PyErr_Occurred()) {
Packit 130fc8
        PyErr_Print();
Packit 130fc8
    }
Packit 130fc8
    PyGILState_Release(gil);
Packit 130fc8
    return ret;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
static const DBusObjectPathVTable _object_path_vtable = {
Packit 130fc8
    _object_path_unregister,
Packit 130fc8
    _object_path_message,
Packit 130fc8
};
Packit 130fc8
Packit 130fc8
static DBusHandlerResult
Packit 130fc8
_filter_message(DBusConnection *conn, DBusMessage *message, void *user_data)
Packit 130fc8
{
Packit 130fc8
    DBusHandlerResult ret;
Packit 130fc8
    PyGILState_STATE gil = PyGILState_Ensure();
Packit 130fc8
    Connection *conn_obj = NULL;
Packit 130fc8
    PyObject *callable = NULL;
Packit 130fc8
    PyObject *msg_obj;
Packit 130fc8
#ifndef DBUS_PYTHON_DISABLE_CHECKS
Packit 130fc8
    Py_ssize_t i, size;
Packit 130fc8
#endif
Packit 130fc8
Packit 130fc8
    dbus_message_ref(message);
Packit 130fc8
    msg_obj = DBusPyMessage_ConsumeDBusMessage(message);
Packit 130fc8
    if (!msg_obj) {
Packit 130fc8
        DBG("%s", "OOM while trying to construct Message");
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
Packit 130fc8
    if (!conn_obj) {
Packit 130fc8
        DBG("%s", "failed to traverse DBusConnection -> Connection weakref");
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
    TRACE(conn_obj);
Packit 130fc8
Packit 130fc8
    /* The user_data is a pointer to a Python object. To avoid
Packit 130fc8
     * cross-library reference cycles, the DBusConnection isn't allowed
Packit 130fc8
     * to reference it. However, as long as the Connection is still
Packit 130fc8
     * alive, its ->filters list owns a reference to the same Python
Packit 130fc8
     * object, so the object should also still be alive.
Packit 130fc8
     *
Packit 130fc8
     * To ensure that this works, be careful whenever manipulating the
Packit 130fc8
     * filters list! (always put things in the list *before* giving
Packit 130fc8
     * them to libdbus, etc.)
Packit 130fc8
     */
Packit 130fc8
#ifdef DBUS_PYTHON_DISABLE_CHECKS
Packit 130fc8
    callable = (PyObject *)user_data;
Packit 130fc8
#else
Packit 130fc8
    size = PyList_GET_SIZE(conn_obj->filters);
Packit 130fc8
    for (i = 0; i < size; i++) {
Packit 130fc8
        callable = PyList_GET_ITEM(conn_obj->filters, i);
Packit 130fc8
        if (callable == user_data) {
Packit 130fc8
            Py_INCREF(callable);
Packit 130fc8
            break;
Packit 130fc8
        }
Packit 130fc8
        else {
Packit 130fc8
            callable = NULL;
Packit 130fc8
        }
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    if (!callable) {
Packit 130fc8
        DBG("... filter %p has vanished from ->filters, so not calling it",
Packit 130fc8
            user_data);
Packit 130fc8
        ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
Packit 130fc8
        goto out;
Packit 130fc8
    }
Packit 130fc8
#endif
Packit 130fc8
Packit 130fc8
    ret = DBusPyConnection_HandleMessage(conn_obj, msg_obj, callable);
Packit 130fc8
out:
Packit 130fc8
    Py_CLEAR(msg_obj);
Packit 130fc8
    Py_CLEAR(conn_obj);
Packit 130fc8
    Py_CLEAR(callable);
Packit 130fc8
    PyGILState_Release(gil);
Packit 130fc8
    return ret;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection__require_main_loop__doc__,
Packit 130fc8
"_require_main_loop()\n\n"
Packit 130fc8
"Raise an exception if this Connection is not bound to any main loop -\n"
Packit 130fc8
"in this state, asynchronous calls, receiving signals and exporting objects\n"
Packit 130fc8
"will not work.\n"
Packit 130fc8
"\n"
Packit 130fc8
"`dbus.mainloop.NULL_MAIN_LOOP` is treated like a valid main loop - if you're\n"
Packit 130fc8
"using that, you presumably know what you're doing.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection__require_main_loop (Connection *self, PyObject *args UNUSED)
Packit 130fc8
{
Packit 130fc8
    if (!self->has_mainloop) {
Packit 130fc8
        PyErr_SetString(PyExc_RuntimeError,
Packit 130fc8
                        "To make asynchronous calls, receive signals or "
Packit 130fc8
                        "export objects, D-Bus connections must be attached "
Packit 130fc8
                        "to a main loop by passing mainloop=... to the "
Packit 130fc8
                        "constructor or calling "
Packit 130fc8
                        "dbus.set_default_main_loop(...)");
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_close__doc__,
Packit 130fc8
"close()\n\n"
Packit 130fc8
"Close the connection.");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_close (Connection *self, PyObject *args UNUSED)
Packit 130fc8
{
Packit 130fc8
    TRACE(self);
Packit 130fc8
    /* Because the user explicitly asked to close the connection, we'll even
Packit 130fc8
    let them close shared connections. */
Packit 130fc8
    if (self->conn) {
Packit 130fc8
        Py_BEGIN_ALLOW_THREADS
Packit 130fc8
        dbus_connection_close(self->conn);
Packit 130fc8
        Py_END_ALLOW_THREADS
Packit 130fc8
    }
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_get_is_connected__doc__,
Packit 130fc8
"get_is_connected() -> bool\n\n"
Packit 130fc8
"Return true if this Connection is connected.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_get_is_connected (Connection *self, PyObject *args UNUSED)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ret;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ret = dbus_connection_get_is_connected(self->conn);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    return PyBool_FromLong(ret);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_get_is_authenticated__doc__,
Packit 130fc8
"get_is_authenticated() -> bool\n\n"
Packit 130fc8
"Return true if this Connection was ever authenticated.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_get_is_authenticated (Connection *self, PyObject *args UNUSED)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ret;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ret = dbus_connection_get_is_authenticated(self->conn);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    return PyBool_FromLong(ret);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_set_exit_on_disconnect__doc__,
Packit 130fc8
"set_exit_on_disconnect(bool)\n\n"
Packit 130fc8
"Set whether the C function ``_exit`` will be called when this Connection\n"
Packit 130fc8
"becomes disconnected. This will cause the program to exit without calling\n"
Packit 130fc8
"any cleanup code or exit handlers.\n"
Packit 130fc8
"\n"
Packit 130fc8
"The default is for this feature to be disabled for Connections and enabled\n"
Packit 130fc8
"for Buses.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_set_exit_on_disconnect (Connection *self, PyObject *args)
Packit 130fc8
{
Packit 130fc8
    int exit_on_disconnect;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTuple(args, "i:set_exit_on_disconnect",
Packit 130fc8
                          &exit_on_disconnect)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    dbus_connection_set_exit_on_disconnect(self->conn,
Packit 130fc8
                                           exit_on_disconnect ? 1 : 0);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_send_message__doc__,
Packit 130fc8
"send_message(msg) -> long\n\n"
Packit 130fc8
"Queue the given message for sending, and return the message serial number.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Parameters:\n"
Packit 130fc8
"   `msg` : dbus.lowlevel.Message\n"
Packit 130fc8
"       The message to be sent.\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_send_message(Connection *self, PyObject *args)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
    PyObject *obj;
Packit 130fc8
    DBusMessage *msg;
Packit 130fc8
    dbus_uint32_t serial;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTuple(args, "O", &obj)) return NULL;
Packit 130fc8
Packit 130fc8
    msg = DBusPyMessage_BorrowDBusMessage(obj);
Packit 130fc8
    if (!msg) return NULL;
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_send(self->conn, msg, &serial);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (!ok) {
Packit 130fc8
        return PyErr_NoMemory();
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    return PyLong_FromUnsignedLong(serial);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_set_allow_anonymous__doc__,
Packit 130fc8
"set_allow_anonymous(bool)\n\n"
Packit 130fc8
"Allows anonymous clients. Call this on the server side of a connection in a on_connection_added callback"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_set_allow_anonymous(Connection *self, PyObject *args)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t t;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTuple(args, "i", &t)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    dbus_connection_set_allow_anonymous(self->conn, t ? 1 : 0);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
/* The timeout is in seconds here, since that's conventional in Python. */
Packit 130fc8
PyDoc_STRVAR(Connection_send_message_with_reply__doc__,
Packit 130fc8
"send_message_with_reply(msg, reply_handler, timeout_s=-1, "
Packit 130fc8
"require_main_loop=False) -> dbus.lowlevel.PendingCall\n\n"
Packit 130fc8
"Queue the message for sending; expect a reply via the returned PendingCall,\n"
Packit 130fc8
"which can also be used to cancel the pending call.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Parameters:\n"
Packit 130fc8
"   `msg` : dbus.lowlevel.Message\n"
Packit 130fc8
"       The message to be sent\n"
Packit 130fc8
"   `reply_handler` : callable\n"
Packit 130fc8
"       Asynchronous reply handler: will be called with one positional\n"
Packit 130fc8
"       parameter, a Message instance representing the reply.\n"
Packit 130fc8
"   `timeout_s` : float\n"
Packit 130fc8
"       If the reply takes more than this many seconds, a timeout error\n"
Packit 130fc8
"       will be created locally and raised instead. If this timeout is\n"
Packit 130fc8
"       negative (default), a sane default (supplied by libdbus) is used.\n"
Packit 130fc8
"   `require_main_loop` : bool\n"
Packit 130fc8
"       If True, raise RuntimeError if this Connection does not have a main\n"
Packit 130fc8
"       loop configured. If False (default) and there is no main loop, you are\n"
Packit 130fc8
"       responsible for calling block() on the PendingCall.\n"
Packit 130fc8
"\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_send_message_with_reply(Connection *self, PyObject *args, PyObject *kw)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
    double timeout_s = -1.0;
Packit 130fc8
    int timeout_ms;
Packit 130fc8
    PyObject *obj, *callable;
Packit 130fc8
    DBusMessage *msg;
Packit 130fc8
    DBusPendingCall *pending;
Packit 130fc8
    int require_main_loop = 0;
Packit 130fc8
    static char *argnames[] = {"msg", "reply_handler", "timeout_s",
Packit 130fc8
                               "require_main_loop", NULL};
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTupleAndKeywords(args, kw,
Packit 130fc8
                                     "OO|di:send_message_with_reply",
Packit 130fc8
                                     argnames,
Packit 130fc8
                                     &obj, &callable, &timeout_s,
Packit 130fc8
                                     &require_main_loop)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    if (require_main_loop && !Connection__require_main_loop(self, NULL)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    msg = DBusPyMessage_BorrowDBusMessage(obj);
Packit 130fc8
    if (!msg) return NULL;
Packit 130fc8
Packit 130fc8
    if (timeout_s < 0) {
Packit 130fc8
        timeout_ms = -1;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        if (timeout_s > ((double)INT_MAX) / 1000.0) {
Packit 130fc8
            PyErr_SetString(PyExc_ValueError, "Timeout too long");
Packit 130fc8
            return NULL;
Packit 130fc8
        }
Packit 130fc8
        timeout_ms = (int)(timeout_s * 1000.0);
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_send_with_reply(self->conn, msg, &pending,
Packit 130fc8
                                         timeout_ms);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (!ok) {
Packit 130fc8
        return PyErr_NoMemory();
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    if (!pending) {
Packit 130fc8
        /* connection is disconnected (doesn't return FALSE!) */
Packit 130fc8
        return DBusPyException_SetString ("Connection is disconnected - "
Packit 130fc8
                                          "unable to make method call");
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    return DBusPyPendingCall_ConsumeDBusPendingCall(pending, callable);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
/* Again, the timeout is in seconds, since that's conventional in Python. */
Packit 130fc8
PyDoc_STRVAR(Connection_send_message_with_reply_and_block__doc__,
Packit 130fc8
"send_message_with_reply_and_block(msg, timeout_s=-1)"
Packit 130fc8
" -> dbus.lowlevel.Message\n\n"
Packit 130fc8
"Send the message and block while waiting for a reply.\n"
Packit 130fc8
"\n"
Packit 130fc8
"This does not re-enter the main loop, so it can lead to a deadlock, if\n"
Packit 130fc8
"the called method tries to make a synchronous call to a method in this\n"
Packit 130fc8
"application. As such, it's probably a bad idea.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Parameters:\n"
Packit 130fc8
"   `msg` : dbus.lowlevel.Message\n"
Packit 130fc8
"       The message to be sent\n"
Packit 130fc8
"   `timeout_s` : float\n"
Packit 130fc8
"       If the reply takes more than this many seconds, a timeout error\n"
Packit 130fc8
"       will be created locally and raised instead. If this timeout is\n"
Packit 130fc8
"       negative (default), a sane default (supplied by libdbus) is used.\n"
Packit 130fc8
":Returns:\n"
Packit 130fc8
"   A `dbus.lowlevel.Message` instance (probably a `dbus.lowlevel.MethodReturnMessage`) on success\n"
Packit 130fc8
":Raises dbus.DBusException:\n"
Packit 130fc8
"   On error (including if the reply arrives but is an\n"
Packit 130fc8
"   error message)\n"
Packit 130fc8
"\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_send_message_with_reply_and_block(Connection *self, PyObject *args)
Packit 130fc8
{
Packit 130fc8
    double timeout_s = -1.0;
Packit 130fc8
    int timeout_ms;
Packit 130fc8
    PyObject *obj;
Packit 130fc8
    DBusMessage *msg, *reply;
Packit 130fc8
    DBusError error;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTuple(args, "O|d:send_message_with_reply_and_block", &obj,
Packit 130fc8
                          &timeout_s)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    msg = DBusPyMessage_BorrowDBusMessage(obj);
Packit 130fc8
    if (!msg) return NULL;
Packit 130fc8
Packit 130fc8
    if (timeout_s < 0) {
Packit 130fc8
        timeout_ms = -1;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        if (timeout_s > ((double)INT_MAX) / 1000.0) {
Packit 130fc8
            PyErr_SetString(PyExc_ValueError, "Timeout too long");
Packit 130fc8
            return NULL;
Packit 130fc8
        }
Packit 130fc8
        timeout_ms = (int)(timeout_s * 1000.0);
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    dbus_error_init(&error);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    reply = dbus_connection_send_with_reply_and_block(self->conn, msg,
Packit 130fc8
                                                      timeout_ms, &error);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    /* FIXME: if we instead used send_with_reply and blocked on the resulting
Packit 130fc8
     * PendingCall, then we could get all args from the error, not just
Packit 130fc8
     * the first */
Packit 130fc8
    if (!reply) {
Packit 130fc8
        return DBusPyException_ConsumeError(&error);
Packit 130fc8
    }
Packit 130fc8
    return DBusPyMessage_ConsumeDBusMessage(reply);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_flush__doc__,
Packit 130fc8
"flush()\n\n"
Packit 130fc8
"Block until the outgoing message queue is empty.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_flush (Connection *self, PyObject *args UNUSED)
Packit 130fc8
{
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    dbus_connection_flush (self->conn);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
/* Unsupported:
Packit 130fc8
 * dbus_connection_preallocate_send
Packit 130fc8
 * dbus_connection_free_preallocated_send
Packit 130fc8
 * dbus_connection_send_preallocated
Packit 130fc8
 * dbus_connection_borrow_message
Packit 130fc8
 * dbus_connection_return_message
Packit 130fc8
 * dbus_connection_steal_borrowed_message
Packit 130fc8
 * dbus_connection_pop_message
Packit 130fc8
 */
Packit 130fc8
Packit 130fc8
/* Non-main-loop handling not yet implemented: */
Packit 130fc8
    /* dbus_connection_read_write_dispatch */
Packit 130fc8
    /* dbus_connection_read_write */
Packit 130fc8
Packit 130fc8
/* Main loop handling not yet implemented: */
Packit 130fc8
    /* dbus_connection_get_dispatch_status */
Packit 130fc8
    /* dbus_connection_dispatch */
Packit 130fc8
    /* dbus_connection_set_watch_functions */
Packit 130fc8
    /* dbus_connection_set_timeout_functions */
Packit 130fc8
    /* dbus_connection_set_wakeup_main_function */
Packit 130fc8
    /* dbus_connection_set_dispatch_status_function */
Packit 130fc8
Packit 130fc8
/* Normally in Python this would be called fileno(), but I don't want to
Packit 130fc8
 * encourage people to select() on it */
Packit 130fc8
PyDoc_STRVAR(Connection_get_unix_fd__doc__,
Packit 130fc8
"get_unix_fd() -> int or None\n\n"
Packit 130fc8
"Get the connection's UNIX file descriptor, if any.\n\n"
Packit 130fc8
"This can be used for SELinux access control checks with ``getpeercon()``\n"
Packit 130fc8
"for example. **Do not** read or write to the file descriptor, or try to\n"
Packit 130fc8
"``select()`` on it.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_get_unix_fd (Connection *self, PyObject *unused UNUSED)
Packit 130fc8
{
Packit 130fc8
    int fd;
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_get_unix_fd (self->conn, &fd;;
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    if (!ok) Py_RETURN_NONE;
Packit 130fc8
    return NATIVEINT_FROMLONG(fd);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_get_peer_unix_user__doc__,
Packit 130fc8
"get_peer_unix_user() -> long or None\n\n"
Packit 130fc8
"Get the UNIX user ID at the other end of the connection, if it has been\n"
Packit 130fc8
"authenticated. Return None if this is a non-UNIX platform or the\n"
Packit 130fc8
"connection has not been authenticated.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_get_peer_unix_user (Connection *self, PyObject *unused UNUSED)
Packit 130fc8
{
Packit 130fc8
    unsigned long uid;
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_get_unix_user (self->conn, &uid);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    if (!ok) Py_RETURN_NONE;
Packit 130fc8
    return PyLong_FromUnsignedLong (uid);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_get_peer_unix_process_id__doc__,
Packit 130fc8
"get_peer_unix_process_id() -> long or None\n\n"
Packit 130fc8
"Get the UNIX process ID at the other end of the connection, if it has been\n"
Packit 130fc8
"authenticated. Return None if this is a non-UNIX platform or the\n"
Packit 130fc8
"connection has not been authenticated.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_get_peer_unix_process_id (Connection *self, PyObject *unused UNUSED)
Packit 130fc8
{
Packit 130fc8
    unsigned long pid;
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_get_unix_process_id (self->conn, &pid;;
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
    if (!ok) Py_RETURN_NONE;
Packit 130fc8
    return PyLong_FromUnsignedLong (pid);
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
/* TODO: wrap dbus_connection_set_unix_user_function Pythonically */
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_add_message_filter__doc__,
Packit 130fc8
"add_message_filter(callable)\n\n"
Packit 130fc8
"Add the given message filter to the internal list.\n\n"
Packit 130fc8
"Filters are handlers that are run on all incoming messages, prior to the\n"
Packit 130fc8
"objects registered to handle object paths.\n"
Packit 130fc8
"\n"
Packit 130fc8
"Filters are run in the order that they were added. The same handler can\n"
Packit 130fc8
"be added as a filter more than once, in which case it will be run more\n"
Packit 130fc8
"than once. Filters added during a filter callback won't be run on the\n"
Packit 130fc8
"message being processed.\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_add_message_filter(Connection *self, PyObject *callable)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    /* The callable must be referenced by ->filters *before* it is
Packit 130fc8
     * given to libdbus, which does not own a reference to it.
Packit 130fc8
     */
Packit 130fc8
    if (PyList_Append(self->filters, callable) < 0) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_add_filter(self->conn, _filter_message, callable,
Packit 130fc8
                                    NULL);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (!ok) {
Packit 130fc8
        Py_XDECREF(PyObject_CallMethod(self->filters, "remove", "(O)",
Packit 130fc8
                                       callable));
Packit 130fc8
        PyErr_NoMemory();
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_remove_message_filter__doc__,
Packit 130fc8
"remove_message_filter(callable)\n\n"
Packit 130fc8
"Remove the given message filter (see `add_message_filter` for details).\n"
Packit 130fc8
"\n"
Packit 130fc8
":Raises LookupError:\n"
Packit 130fc8
"   The given callable is not among the registered filters\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_remove_message_filter(Connection *self, PyObject *callable)
Packit 130fc8
{
Packit 130fc8
    PyObject *obj;
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    /* It's safe to do this before removing it from libdbus, because
Packit 130fc8
     * the presence of callable in our arguments means we have a ref
Packit 130fc8
     * to it. */
Packit 130fc8
    obj = PyObject_CallMethod(self->filters, "remove", "(O)", callable);
Packit 130fc8
    if (!obj) return NULL;
Packit 130fc8
    Py_CLEAR(obj);
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    dbus_connection_remove_filter(self->conn, _filter_message, callable);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    Py_RETURN_NONE;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection__register_object_path__doc__,
Packit 130fc8
"register_object_path(path, on_message, on_unregister=None, fallback=False)\n"
Packit 130fc8
"\n"
Packit 130fc8
"Register a callback to be called when messages arrive at the given\n"
Packit 130fc8
"object-path. Used to export objects' methods on the bus in a low-level\n"
Packit 130fc8
"way. For the high-level interface to this functionality (usually\n"
Packit 130fc8
"recommended) see the `dbus.service.Object` base class.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Parameters:\n"
Packit 130fc8
"   `path` : str\n"
Packit 130fc8
"       Object path to be acted on\n"
Packit 130fc8
"   `on_message` : callable\n"
Packit 130fc8
"       Called when a message arrives at the given object-path, with\n"
Packit 130fc8
"       two positional parameters: the first is this Connection,\n"
Packit 130fc8
"       the second is the incoming `dbus.lowlevel.Message`.\n"
Packit 130fc8
"   `on_unregister` : callable or None\n"
Packit 130fc8
"       If not None, called when the callback is unregistered.\n"
Packit 130fc8
"   `fallback` : bool\n"
Packit 130fc8
"       If True (the default is False), when a message arrives for a\n"
Packit 130fc8
"       'subdirectory' of the given path and there is no more specific\n"
Packit 130fc8
"       handler, use this handler. Normally this handler is only run if\n"
Packit 130fc8
"       the paths match exactly.\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection__register_object_path(Connection *self, PyObject *args,
Packit 130fc8
                                 PyObject *kwargs)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
    int fallback = 0;
Packit 130fc8
    char *path_bytes;
Packit 130fc8
    PyObject *callbacks, *path, *tuple, *on_message, *on_unregister = Py_None;
Packit 130fc8
    static char *argnames[] = {"path", "on_message", "on_unregister",
Packit 130fc8
                               "fallback", NULL};
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!Connection__require_main_loop(self, NULL)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
Packit 130fc8
                                     "OO|Oi:_register_object_path",
Packit 130fc8
                                     argnames,
Packit 130fc8
                                     &path,
Packit 130fc8
                                     &on_message, &on_unregister,
Packit 130fc8
                                     &fallback)) return NULL;
Packit 130fc8
Packit 130fc8
    /* Take a reference to path, which we give away to libdbus in a moment.
Packit 130fc8
Packit 130fc8
    Also, path needs to be a string (not a subclass which could do something
Packit 130fc8
    mad) to preserve the desirable property that the DBusConnection can
Packit 130fc8
    never strongly reference the Connection, even indirectly.
Packit 130fc8
    */
Packit 130fc8
    if (PyBytes_CheckExact(path)) {
Packit 130fc8
        Py_INCREF(path);
Packit 130fc8
    }
Packit 130fc8
    else if (PyUnicode_Check(path)) {
Packit 130fc8
        path = PyUnicode_AsUTF8String(path);
Packit 130fc8
        if (!path) return NULL;
Packit 130fc8
    }
Packit 130fc8
    else if (PyBytes_Check(path)) {
Packit 130fc8
        path = PyBytes_FromString(PyBytes_AS_STRING(path));
Packit 130fc8
        if (!path) return NULL;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        PyErr_SetString(PyExc_TypeError,
Packit 130fc8
                        "path must be a str, bytes, or unicode object");
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    path_bytes = PyBytes_AS_STRING(path);
Packit 130fc8
    if (!dbus_py_validate_object_path(path_bytes)) {
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    tuple = Py_BuildValue("(OO)", on_unregister, on_message);
Packit 130fc8
    if (!tuple) {
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    /* Guard against registering a handler that already exists. */
Packit 130fc8
    callbacks = PyDict_GetItem(self->object_paths, path);
Packit 130fc8
    if (callbacks && callbacks != Py_None) {
Packit 130fc8
        PyErr_Format(PyExc_KeyError, "Can't register the object-path "
Packit 130fc8
                     "handler for '%s': there is already a handler",
Packit 130fc8
                     path_bytes);
Packit 130fc8
        Py_CLEAR(tuple);
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    /* Pre-allocate a slot in the dictionary, so we know we'll be able
Packit 130fc8
     * to replace it with the callbacks without OOM.
Packit 130fc8
     * This ensures we can keep libdbus' opinion of whether those
Packit 130fc8
     * paths are handled in sync with our own. */
Packit 130fc8
    if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
Packit 130fc8
        Py_CLEAR(tuple);
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    if (fallback) {
Packit 130fc8
        ok = dbus_connection_register_fallback(self->conn,
Packit 130fc8
                                               path_bytes,
Packit 130fc8
                                               &_object_path_vtable,
Packit 130fc8
                                               path);
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        ok = dbus_connection_register_object_path(self->conn,
Packit 130fc8
                                                  path_bytes,
Packit 130fc8
                                                  &_object_path_vtable,
Packit 130fc8
                                                  path);
Packit 130fc8
    }
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (ok) {
Packit 130fc8
        if (PyDict_SetItem(self->object_paths, path, tuple) < 0) {
Packit 130fc8
            /* That shouldn't have happened, we already allocated enough
Packit 130fc8
            memory for it. Oh well, try to undo the registration to keep
Packit 130fc8
            things in sync. If this fails too, we've leaked a bit of
Packit 130fc8
            memory in libdbus, but tbh we should never get here anyway. */
Packit 130fc8
            Py_BEGIN_ALLOW_THREADS
Packit 130fc8
            ok = dbus_connection_unregister_object_path(self->conn,
Packit 130fc8
                                                        path_bytes);
Packit 130fc8
            Py_END_ALLOW_THREADS
Packit 130fc8
            return NULL;
Packit 130fc8
        }
Packit 130fc8
        /* don't DECREF path: libdbus owns a ref now */
Packit 130fc8
        Py_CLEAR(tuple);
Packit 130fc8
        Py_RETURN_NONE;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        /* Oops, OOM. Tidy up, if we can, ignoring any error. */
Packit 130fc8
        PyDict_DelItem(self->object_paths, path);
Packit 130fc8
        PyErr_Clear();
Packit 130fc8
        Py_CLEAR(tuple);
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        PyErr_NoMemory();
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection__unregister_object_path__doc__,
Packit 130fc8
"unregister_object_path(path)\n\n"
Packit 130fc8
"Remove a previously registered handler for the given object path.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Parameters:\n"
Packit 130fc8
"   `path` : str\n"
Packit 130fc8
"       The object path whose handler is to be removed\n"
Packit 130fc8
":Raises KeyError: if there is no handler registered for exactly that\n"
Packit 130fc8
"   object path.\n"
Packit 130fc8
);
Packit 130fc8
static PyObject *
Packit 130fc8
Connection__unregister_object_path(Connection *self, PyObject *args,
Packit 130fc8
                                   PyObject *kwargs)
Packit 130fc8
{
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
    char *path_bytes;
Packit 130fc8
    PyObject *path;
Packit 130fc8
    PyObject *callbacks;
Packit 130fc8
    static char *argnames[] = {"path", NULL};
Packit 130fc8
Packit 130fc8
    TRACE(self);
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
Packit 130fc8
                                     "O:_unregister_object_path",
Packit 130fc8
                                     argnames, &path)) return NULL;
Packit 130fc8
Packit 130fc8
    /* Take a ref to the path. Same comments as for _register_object_path. */
Packit 130fc8
    if (PyBytes_CheckExact(path)) {
Packit 130fc8
        Py_INCREF(path);
Packit 130fc8
    }
Packit 130fc8
    else if (PyUnicode_Check(path)) {
Packit 130fc8
        path = PyUnicode_AsUTF8String(path);
Packit 130fc8
        if (!path) return NULL;
Packit 130fc8
    }
Packit 130fc8
    else if (PyBytes_Check(path)) {
Packit 130fc8
        path = PyBytes_FromString(PyBytes_AS_STRING(path));
Packit 130fc8
        if (!path) return NULL;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        PyErr_SetString(PyExc_TypeError,
Packit 130fc8
                        "path must be a str, bytes, or unicode object");
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    path_bytes = PyBytes_AS_STRING(path);
Packit 130fc8
Packit 130fc8
    /* Guard against unregistering a handler that doesn't, in fact, exist,
Packit 130fc8
    or whose unregistration is already in progress. */
Packit 130fc8
    callbacks = PyDict_GetItem(self->object_paths, path);
Packit 130fc8
    if (!callbacks || callbacks == Py_None) {
Packit 130fc8
        PyErr_Format(PyExc_KeyError, "Can't unregister the object-path "
Packit 130fc8
                     "handler for '%s': there is no such handler",
Packit 130fc8
                     path_bytes);
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    /* Hang on to a reference to the callbacks for the moment. */
Packit 130fc8
    Py_INCREF(callbacks);
Packit 130fc8
Packit 130fc8
    /* Get rid of the object-path while we still have the GIL, to
Packit 130fc8
    guard against unregistering twice from different threads (which
Packit 130fc8
    causes undefined behaviour in libdbus).
Packit 130fc8
Packit 130fc8
    Because deletion would make it possible for the re-insertion below
Packit 130fc8
    to fail, we instead set the handler to None as a placeholder.
Packit 130fc8
    */
Packit 130fc8
    if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
Packit 130fc8
        /* If that failed, there's no need to be paranoid as below - the
Packit 130fc8
        callbacks are still set, so we failed, but at least everything
Packit 130fc8
        is in sync. */
Packit 130fc8
        Py_CLEAR(callbacks);
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    /* BEGIN PARANOIA
Packit 130fc8
    This is something of a critical section - the dict of object-paths
Packit 130fc8
    and libdbus' internal structures are out of sync for a bit. We have
Packit 130fc8
    to be able to cope with that.
Packit 130fc8
Packit 130fc8
    It's really annoying that dbus_connection_unregister_object_path
Packit 130fc8
    can fail, *and* has undefined behaviour if the object path has
Packit 130fc8
    already been unregistered. Either/or would be fine.
Packit 130fc8
    */
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_unregister_object_path(self->conn, path_bytes);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (ok) {
Packit 130fc8
        Py_CLEAR(callbacks);
Packit 130fc8
        PyDict_DelItem(self->object_paths, path);
Packit 130fc8
        /* END PARANOIA on successful code path */
Packit 130fc8
        /* The above can't fail unless by some strange trickery the key is no
Packit 130fc8
        longer present. Ignore any errors. */
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        PyErr_Clear();
Packit 130fc8
        Py_RETURN_NONE;
Packit 130fc8
    }
Packit 130fc8
    else {
Packit 130fc8
        /* Oops, OOM. Put the callbacks back in the dict so
Packit 130fc8
         * we'll have another go if/when the user frees some memory
Packit 130fc8
         * and tries calling this method again. */
Packit 130fc8
        PyDict_SetItem(self->object_paths, path, callbacks);
Packit 130fc8
        /* END PARANOIA on failing code path */
Packit 130fc8
        /* If the SetItem failed, there's nothing we can do about it - but
Packit 130fc8
        since we know it's an existing entry, it shouldn't be able to fail
Packit 130fc8
        anyway. */
Packit 130fc8
        Py_CLEAR(path);
Packit 130fc8
        Py_CLEAR(callbacks);
Packit 130fc8
        return PyErr_NoMemory();
Packit 130fc8
    }
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(Connection_list_exported_child_objects__doc__,
Packit 130fc8
"list_exported_child_objects(path: str) -> list of str\n\n"
Packit 130fc8
"Return a list of the names of objects exported on this Connection as\n"
Packit 130fc8
"direct children of the given object path.\n"
Packit 130fc8
"\n"
Packit 130fc8
"Each name returned may be converted to a valid object path using\n"
Packit 130fc8
"``dbus.ObjectPath('%s%s%s' % (path, (path != '/' and '/' or ''), name))``.\n"
Packit 130fc8
"For the purposes of this function, every parent or ancestor of an exported\n"
Packit 130fc8
"object is considered to be an exported object, even if it's only an object\n"
Packit 130fc8
"synthesized by the library to support introspection.\n");
Packit 130fc8
static PyObject *
Packit 130fc8
Connection_list_exported_child_objects (Connection *self, PyObject *args,
Packit 130fc8
                                        PyObject *kwargs)
Packit 130fc8
{
Packit 130fc8
    const char *path;
Packit 130fc8
    char **kids, **kid_ptr;
Packit 130fc8
    dbus_bool_t ok;
Packit 130fc8
    PyObject *ret;
Packit 130fc8
    static char *argnames[] = {"path", NULL};
Packit 130fc8
Packit 130fc8
    DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
Packit 130fc8
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", argnames, &path)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    if (!dbus_py_validate_object_path(path)) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    Py_BEGIN_ALLOW_THREADS
Packit 130fc8
    ok = dbus_connection_list_registered(self->conn, path, &kids);
Packit 130fc8
    Py_END_ALLOW_THREADS
Packit 130fc8
Packit 130fc8
    if (!ok) {
Packit 130fc8
        return PyErr_NoMemory();
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    ret = PyList_New(0);
Packit 130fc8
    if (!ret) {
Packit 130fc8
        return NULL;
Packit 130fc8
    }
Packit 130fc8
    for (kid_ptr = kids; *kid_ptr; kid_ptr++) {
Packit 130fc8
        PyObject *tmp = NATIVESTR_FROMSTR(*kid_ptr);
Packit 130fc8
Packit 130fc8
        if (!tmp) {
Packit 130fc8
            Py_CLEAR(ret);
Packit 130fc8
            return NULL;
Packit 130fc8
        }
Packit 130fc8
        if (PyList_Append(ret, tmp) < 0) {
Packit 130fc8
            Py_CLEAR(tmp);
Packit 130fc8
            Py_CLEAR(ret);
Packit 130fc8
            return NULL;
Packit 130fc8
        }
Packit 130fc8
        Py_CLEAR(tmp);
Packit 130fc8
    }
Packit 130fc8
Packit 130fc8
    dbus_free_string_array(kids);
Packit 130fc8
Packit 130fc8
    return ret;
Packit 130fc8
}
Packit 130fc8
Packit 130fc8
    /* dbus_connection_get_object_path_data - not useful to Python,
Packit 130fc8
     * the object path data is just a PyBytes containing the path */
Packit 130fc8
    /* dbus_connection_list_registered could be useful, though */
Packit 130fc8
Packit 130fc8
/* dbus_connection_set_change_sigpipe - sets global state */
Packit 130fc8
Packit 130fc8
/* Maxima. Does Python code ever need to manipulate these?
Packit 130fc8
 * OTOH they're easy to wrap */
Packit 130fc8
    /* dbus_connection_set_max_message_size */
Packit 130fc8
    /* dbus_connection_get_max_message_size */
Packit 130fc8
    /* dbus_connection_set_max_received_size */
Packit 130fc8
    /* dbus_connection_get_max_received_size */
Packit 130fc8
Packit 130fc8
/* dbus_connection_get_outgoing_size - almost certainly unneeded */
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(new_for_bus__doc__,
Packit 130fc8
"Connection._new_for_bus([address: str or int]) -> Connection\n"
Packit 130fc8
"\n"
Packit 130fc8
"If the address is an int it must be one of the constants BUS_SESSION,\n"
Packit 130fc8
"BUS_SYSTEM, BUS_STARTER; if a string, it must be a D-Bus address.\n"
Packit 130fc8
"The default is BUS_SESSION.\n"
Packit 130fc8
);
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(get_unique_name__doc__,
Packit 130fc8
"get_unique_name() -> str\n\n"
Packit 130fc8
"Return this application's unique name on this bus.\n"
Packit 130fc8
"\n"
Packit 130fc8
":Raises DBusException: if the connection has no unique name yet\n"
Packit 130fc8
"   (for Bus objects this can't happen, for peer-to-peer connections\n"
Packit 130fc8
"   this means you haven't called `set_unique_name`)\n");
Packit 130fc8
Packit 130fc8
PyDoc_STRVAR(set_unique_name__doc__,
Packit 130fc8
"set_unique_name(str)\n\n"
Packit 130fc8
"Set this application's unique name on this bus. Raise ValueError if it has\n"
Packit 130fc8
"already been set.\n");
Packit 130fc8
Packit 130fc8
struct PyMethodDef DBusPyConnection_tp_methods[] = {
Packit 130fc8
#define ENTRY(name, flags) {#name, (PyCFunction)Connection_##name, flags, Connection_##name##__doc__}
Packit 130fc8
    ENTRY(_require_main_loop, METH_NOARGS),
Packit 130fc8
    ENTRY(close, METH_NOARGS),
Packit 130fc8
    ENTRY(flush, METH_NOARGS),
Packit 130fc8
    ENTRY(get_is_connected, METH_NOARGS),
Packit 130fc8
    ENTRY(get_is_authenticated, METH_NOARGS),
Packit 130fc8
    ENTRY(set_exit_on_disconnect, METH_VARARGS),
Packit 130fc8
    ENTRY(get_unix_fd, METH_NOARGS),
Packit 130fc8
    ENTRY(get_peer_unix_user, METH_NOARGS),
Packit 130fc8
    ENTRY(get_peer_unix_process_id, METH_NOARGS),
Packit 130fc8
    ENTRY(add_message_filter, METH_O),
Packit 130fc8
    ENTRY(_register_object_path, METH_VARARGS|METH_KEYWORDS),
Packit 130fc8
    ENTRY(remove_message_filter, METH_O),
Packit 130fc8
    ENTRY(send_message, METH_VARARGS),
Packit 130fc8
    ENTRY(send_message_with_reply, METH_VARARGS|METH_KEYWORDS),
Packit 130fc8
    ENTRY(send_message_with_reply_and_block, METH_VARARGS),
Packit 130fc8
    ENTRY(_unregister_object_path, METH_VARARGS|METH_KEYWORDS),
Packit 130fc8
    ENTRY(list_exported_child_objects, METH_VARARGS|METH_KEYWORDS),
Packit 130fc8
    {"_new_for_bus", (PyCFunction)DBusPyConnection_NewForBus,
Packit 130fc8
        METH_CLASS|METH_VARARGS|METH_KEYWORDS,
Packit 130fc8
        new_for_bus__doc__},
Packit 130fc8
    {"get_unique_name", (PyCFunction)DBusPyConnection_GetUniqueName,
Packit 130fc8
        METH_NOARGS,
Packit 130fc8
        get_unique_name__doc__},
Packit 130fc8
    {"set_unique_name", (PyCFunction)DBusPyConnection_SetUniqueName,
Packit 130fc8
        METH_VARARGS,
Packit 130fc8
        set_unique_name__doc__},
Packit 130fc8
    ENTRY(set_allow_anonymous, METH_VARARGS),
Packit 130fc8
    {NULL},
Packit 130fc8
#undef ENTRY
Packit 130fc8
};
Packit 130fc8
Packit 130fc8
/* vim:set ft=c cino< sw=4 sts=4 et: */