2003-03-20 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-transport.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-transport.c DBusTransport object (internal to D-BUS implementation)
3  *
4  * Copyright (C) 2002, 2003  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-transport-protected.h"
25 #include "dbus-transport-unix.h"
26 #include "dbus-connection-internal.h"
27 #include "dbus-watch.h"
28 #include "dbus-auth.h"
29 #include "dbus-address.h"
30 #ifdef DBUS_BUILD_TESTS
31 #include "dbus-transport-debug.h"
32 #include "dbus-server-debug-pipe.h"
33 #endif
34
35 /**
36  * @defgroup DBusTransport DBusTransport object
37  * @ingroup  DBusInternals
38  * @brief "Backend" for a DBusConnection.
39  *
40  * Types and functions related to DBusTransport.  A transport is an
41  * abstraction that can send and receive data via various kinds of
42  * network connections or other IPC mechanisms.
43  * 
44  * @{
45  */
46
47 /**
48  * @typedef DBusTransport
49  *
50  * Opaque object representing a way message stream.
51  * DBusTransport abstracts various kinds of actual
52  * transport mechanism, such as different network protocols,
53  * or encryption schemes.
54  */
55
56 static void
57 live_messages_size_notify (DBusCounter *counter,
58                            void        *user_data)
59 {
60   DBusTransport *transport = user_data;
61
62   _dbus_transport_ref (transport);
63
64 #if 0
65   _dbus_verbose ("Counter value is now %d\n",
66                  (int) _dbus_counter_get_value (counter));
67 #endif
68   
69   /* disable or re-enable the read watch for the transport if
70    * required.
71    */
72   if (* transport->vtable->live_messages_changed)
73     (* transport->vtable->live_messages_changed) (transport);
74
75   _dbus_transport_unref (transport);
76 }
77
78 /**
79  * Initializes the base class members of DBusTransport.
80  * Chained up to by subclasses in their constructor.
81  *
82  * @param transport the transport being created.
83  * @param vtable the subclass vtable.
84  * @param server #TRUE if this transport is on the server side of a connection
85  * @returns #TRUE on success.
86  */
87 dbus_bool_t
88 _dbus_transport_init_base (DBusTransport             *transport,
89                            const DBusTransportVTable *vtable,
90                            dbus_bool_t                server)
91 {
92   DBusMessageLoader *loader;
93   DBusAuth *auth;
94   DBusCounter *counter;
95   
96   loader = _dbus_message_loader_new ();
97   if (loader == NULL)
98     return FALSE;
99   
100   if (server)
101     auth = _dbus_auth_server_new ();
102   else
103     auth = _dbus_auth_client_new ();
104   if (auth == NULL)
105     {
106       _dbus_message_loader_unref (loader);
107       return FALSE;
108     }
109
110   counter = _dbus_counter_new ();
111   if (counter == NULL)
112     {
113       _dbus_auth_unref (auth);
114       _dbus_message_loader_unref (loader);
115       return FALSE;
116     }
117   
118   transport->refcount = 1;
119   transport->vtable = vtable;
120   transport->loader = loader;
121   transport->auth = auth;
122   transport->live_messages_size = counter;
123   transport->authenticated = FALSE;
124   transport->messages_need_sending = FALSE;
125   transport->disconnected = FALSE;
126   transport->send_credentials_pending = !server;
127   transport->receive_credentials_pending = server;
128   transport->is_server = server;
129
130   transport->unix_user_function = NULL;
131   transport->unix_user_data = NULL;
132   transport->free_unix_user_data = NULL;
133   
134   /* Try to default to something that won't totally hose the system,
135    * but doesn't impose too much of a limitation.
136    */
137   transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
138   
139   transport->credentials.pid = -1;
140   transport->credentials.uid = -1;
141   transport->credentials.gid = -1;
142
143   _dbus_counter_set_notify (transport->live_messages_size,
144                             transport->max_live_messages_size,
145                             live_messages_size_notify,
146                             transport);
147   
148   return TRUE;
149 }
150
151 /**
152  * Finalizes base class members of DBusTransport.
153  * Chained up to from subclass finalizers.
154  *
155  * @param transport the transport.
156  */
157 void
158 _dbus_transport_finalize_base (DBusTransport *transport)
159 {
160   if (!transport->disconnected)
161     _dbus_transport_disconnect (transport);
162
163   if (transport->free_unix_user_data != NULL)
164     (* transport->free_unix_user_data) (transport->unix_user_data);
165   
166   _dbus_message_loader_unref (transport->loader);
167   _dbus_auth_unref (transport->auth);
168   _dbus_counter_set_notify (transport->live_messages_size,
169                             0, NULL, NULL);
170   _dbus_counter_unref (transport->live_messages_size);
171 }
172
173 /**
174  * Opens a new transport for the given address.  (This opens a
175  * client-side-of-the-connection transport.)
176  *
177  * @todo error messages on bad address could really be better.
178  * DBusResultCode is a bit limiting here.
179  * 
180  * @param address the address.
181  * @param result location to store reason for failure.
182  * @returns new transport of #NULL on failure.
183  */
184 DBusTransport*
185 _dbus_transport_open (const char     *address,
186                       DBusResultCode *result)
187 {
188   DBusTransport *transport;
189   DBusAddressEntry **entries;
190   int len, i;
191   
192   if (!dbus_parse_address (address, &entries, &len, result))
193     return NULL;
194
195   transport = NULL;
196   
197   for (i = 0; i < len; i++)
198     {
199       const char *method = dbus_address_entry_get_method (entries[i]);
200
201       if (strcmp (method, "unix") == 0)
202         {
203           const char *path = dbus_address_entry_get_value (entries[i], "path");
204
205           if (path == NULL)
206             goto bad_address;
207
208           transport = _dbus_transport_new_for_domain_socket (path, FALSE, result);
209         }
210       else if (strcmp (method, "tcp") == 0)
211         {
212           const char *host = dbus_address_entry_get_value (entries[i], "host");
213           const char *port = dbus_address_entry_get_value (entries[i], "port");
214           DBusString  str;
215           long lport;
216           dbus_bool_t sresult;
217           
218           if (port == NULL)
219             goto bad_address;
220
221           _dbus_string_init_const (&str, port);
222           sresult = _dbus_string_parse_int (&str, 0, &lport, NULL);
223           _dbus_string_free (&str);
224           
225           if (sresult == FALSE || lport <= 0 || lport > 65535)
226             goto bad_address;
227           
228           transport = _dbus_transport_new_for_tcp_socket (host, lport, FALSE, result);
229         }
230 #ifdef DBUS_BUILD_TESTS
231       else if (strcmp (method, "debug") == 0)
232         {
233           const char *name = dbus_address_entry_get_value (entries[i], "name");
234
235           if (name == NULL)
236             goto bad_address;
237
238           transport = _dbus_transport_debug_client_new (name, result);
239         }
240       else if (strcmp (method, "debug-pipe") == 0)
241         {
242           const char *name = dbus_address_entry_get_value (entries[i], "name");
243
244           if (name == NULL)
245             goto bad_address;
246
247           transport = _dbus_transport_debug_pipe_new (name, result);
248         }
249 #endif
250       else
251         goto bad_address;
252
253       if (transport)
254         break;    
255     }
256   
257   dbus_address_entries_free (entries);
258   return transport;
259
260  bad_address:
261   dbus_address_entries_free (entries);
262   dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
263
264   return NULL;
265 }
266
267 /**
268  * Increments the reference count for the transport.
269  *
270  * @param transport the transport.
271  */
272 void
273 _dbus_transport_ref (DBusTransport *transport)
274 {
275   _dbus_assert (transport->refcount > 0);
276   
277   transport->refcount += 1;
278 }
279
280 /**
281  * Decrements the reference count for the transport.
282  * Disconnects and finalizes the transport if
283  * the reference count reaches zero.
284  *
285  * @param transport the transport.
286  */
287 void
288 _dbus_transport_unref (DBusTransport *transport)
289 {
290   _dbus_assert (transport != NULL);
291   _dbus_assert (transport->refcount > 0);
292
293   transport->refcount -= 1;
294   if (transport->refcount == 0)
295     {
296       _dbus_assert (transport->vtable->finalize != NULL);
297       
298       (* transport->vtable->finalize) (transport);
299     }
300 }
301
302 /**
303  * Closes our end of the connection to a remote application. Further
304  * attempts to use this transport will fail. Only the first call to
305  * _dbus_transport_disconnect() will have an effect.
306  *
307  * @param transport the transport.
308  * 
309  */
310 void
311 _dbus_transport_disconnect (DBusTransport *transport)
312 {
313   _dbus_assert (transport->vtable->disconnect != NULL);
314
315   if (transport->disconnected)
316     return;
317
318   (* transport->vtable->disconnect) (transport);
319   
320   transport->disconnected = TRUE;
321
322   if (transport->connection)
323     _dbus_connection_notify_disconnected (transport->connection);
324 }
325
326 /**
327  * Returns #TRUE if the transport has not been disconnected.
328  * Disconnection can result from _dbus_transport_disconnect()
329  * or because the server drops its end of the connection.
330  *
331  * @param transport the transport.
332  * @returns whether we're connected
333  */
334 dbus_bool_t
335 _dbus_transport_get_is_connected (DBusTransport *transport)
336 {
337   return !transport->disconnected;
338 }
339
340 /**
341  * Returns #TRUE if we have been authenticated.  Will return #TRUE
342  * even if the transport is disconnected.
343  *
344  * @todo needs to drop connection->mutex when calling the unix_user_function
345  *
346  * @param transport the transport
347  * @returns whether we're authenticated
348  */
349 dbus_bool_t
350 _dbus_transport_get_is_authenticated (DBusTransport *transport)
351 {  
352   if (transport->authenticated)
353     return TRUE;
354   else
355     {
356       if (transport->disconnected)
357         return FALSE;
358       
359       transport->authenticated =
360         (!(transport->send_credentials_pending ||
361            transport->receive_credentials_pending)) &&
362         _dbus_auth_do_work (transport->auth) == DBUS_AUTH_STATE_AUTHENTICATED;
363
364       /* If we've authenticated as some identity, check that the auth
365        * identity is the same as our own identity.  In the future, we
366        * may have API allowing applications to specify how this is
367        * done, for example they may allow connection as any identity,
368        * but then impose restrictions on certain identities.
369        * Or they may give certain identities extra privileges.
370        */
371       
372       if (transport->authenticated && transport->is_server)
373         {
374           DBusCredentials auth_identity;
375
376           _dbus_auth_get_identity (transport->auth, &auth_identity);
377
378           if (transport->unix_user_function != NULL)
379             {
380               /* FIXME we hold the connection lock here and should drop it */
381               if (!(* transport->unix_user_function) (transport->connection,
382                                                       auth_identity.uid,
383                                                       transport->unix_user_data))
384                 {
385                   _dbus_verbose ("Client UID %d was rejected, disconnecting\n",
386                                  auth_identity.uid);
387                   _dbus_transport_disconnect (transport);
388                   return FALSE;
389                 }
390               else
391                 {
392                   _dbus_verbose ("Client UID %d authorized\n", auth_identity.uid);
393                 }
394             }
395           else
396             {
397               DBusCredentials our_identity;
398               
399               _dbus_credentials_from_current_process (&our_identity);
400               
401               if (!_dbus_credentials_match (&our_identity,
402                                             &auth_identity))
403                 {
404                   _dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
405                                  auth_identity.uid, our_identity.uid);
406                   _dbus_transport_disconnect (transport);
407                   return FALSE;
408                 }
409               else
410                 {
411                   _dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
412                                  auth_identity.uid, our_identity.uid);
413                 }
414             }
415         }
416       
417       return transport->authenticated;
418     }
419 }
420
421 /**
422  * Handles a watch by reading data, writing data, or disconnecting
423  * the transport, as appropriate for the given condition.
424  *
425  * @param transport the transport.
426  * @param watch the watch.
427  * @param condition the current state of the watched file descriptor.
428  * @returns #FALSE if not enough memory to fully handle the watch
429  */
430 dbus_bool_t
431 _dbus_transport_handle_watch (DBusTransport           *transport,
432                               DBusWatch               *watch,
433                               unsigned int             condition)
434 {
435   dbus_bool_t retval;
436   
437   _dbus_assert (transport->vtable->handle_watch != NULL);
438
439   if (transport->disconnected)
440     return TRUE;
441
442   if (dbus_watch_get_fd (watch) < 0)
443     {
444       _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n");
445       return TRUE;
446     }
447   
448   _dbus_watch_sanitize_condition (watch, &condition);
449
450   _dbus_transport_ref (transport);
451   _dbus_watch_ref (watch);
452   retval = (* transport->vtable->handle_watch) (transport, watch, condition);
453   _dbus_watch_unref (watch);
454   _dbus_transport_unref (transport);
455
456   return retval;
457 }
458
459 /**
460  * Sets the connection using this transport. Allows the transport
461  * to add watches to the connection, queue incoming messages,
462  * and pull outgoing messages.
463  *
464  * @param transport the transport.
465  * @param connection the connection.
466  * @returns #FALSE if not enough memory
467  */
468 dbus_bool_t
469 _dbus_transport_set_connection (DBusTransport  *transport,
470                                 DBusConnection *connection)
471 {
472   _dbus_assert (transport->vtable->connection_set != NULL);
473   _dbus_assert (transport->connection == NULL);
474   
475   transport->connection = connection;
476
477   _dbus_transport_ref (transport);
478   if (!(* transport->vtable->connection_set) (transport))
479     transport->connection = NULL;
480   _dbus_transport_unref (transport);
481
482   return transport->connection != NULL;
483 }
484
485 /**
486  * Notifies the transport when the outgoing message queue goes from
487  * empty to non-empty or vice versa. Typically causes the transport to
488  * add or remove its DBUS_WATCH_WRITABLE watch.
489  *
490  * @param transport the transport.
491  * @param queue_length the length of the outgoing message queue.
492  *
493  */
494 void
495 _dbus_transport_messages_pending (DBusTransport  *transport,
496                                   int             queue_length)
497 {
498   _dbus_assert (transport->vtable->messages_pending != NULL);
499
500   if (transport->disconnected)
501     return;
502
503   transport->messages_need_sending = queue_length > 0;
504
505   _dbus_transport_ref (transport);
506   (* transport->vtable->messages_pending) (transport,
507                                            queue_length);
508   _dbus_transport_unref (transport);
509 }
510
511 /**
512  * Performs a single poll()/select() on the transport's file
513  * descriptors and then reads/writes data as appropriate,
514  * queueing incoming messages and sending outgoing messages.
515  * This is the backend for _dbus_connection_do_iteration().
516  * See _dbus_connection_do_iteration() for full details.
517  *
518  * @param transport the transport.
519  * @param flags indicates whether to read or write, and whether to block.
520  * @param timeout_milliseconds if blocking, timeout or -1 for no timeout.
521  */
522 void
523 _dbus_transport_do_iteration (DBusTransport  *transport,
524                               unsigned int    flags,
525                               int             timeout_milliseconds)
526 {
527   _dbus_assert (transport->vtable->do_iteration != NULL);
528
529   _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n",
530                  flags, timeout_milliseconds, !transport->disconnected);
531   
532   if ((flags & (DBUS_ITERATION_DO_WRITING |
533                 DBUS_ITERATION_DO_READING)) == 0)
534     return; /* Nothing to do */
535
536   if (transport->disconnected)
537     return;
538
539   _dbus_transport_ref (transport);
540   (* transport->vtable->do_iteration) (transport, flags,
541                                        timeout_milliseconds);
542   _dbus_transport_unref (transport);
543 }
544
545 static dbus_bool_t
546 recover_unused_bytes (DBusTransport *transport)
547 {
548   if (_dbus_auth_do_work (transport->auth) != DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES)
549     return TRUE;
550   
551   if (_dbus_auth_needs_decoding (transport->auth))
552     {
553       DBusString plaintext;
554       const DBusString *encoded;
555       DBusString *buffer;
556       int orig_len;
557       
558       if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
559         goto nomem;
560       
561       _dbus_auth_get_unused_bytes (transport->auth,
562                                    &encoded);
563
564       if (!_dbus_auth_decode_data (transport->auth,
565                                    encoded, &plaintext))
566         {
567           _dbus_string_free (&plaintext);
568           goto nomem;
569         }
570       
571       _dbus_message_loader_get_buffer (transport->loader,
572                                        &buffer);
573       
574       orig_len = _dbus_string_get_length (buffer);
575       
576       if (!_dbus_string_move (&plaintext, 0, buffer,
577                               orig_len))
578         {
579           _dbus_string_free (&plaintext);
580           goto nomem;
581         }
582       
583       _dbus_verbose (" %d unused bytes sent to message loader\n", 
584                      _dbus_string_get_length (buffer) -
585                      orig_len);
586       
587       _dbus_message_loader_return_buffer (transport->loader,
588                                           buffer,
589                                           _dbus_string_get_length (buffer) -
590                                           orig_len);
591
592       _dbus_auth_delete_unused_bytes (transport->auth);
593       
594       _dbus_string_free (&plaintext);
595     }
596   else
597     {
598       const DBusString *bytes;
599       DBusString *buffer;
600       int orig_len;
601       dbus_bool_t succeeded;
602
603       _dbus_message_loader_get_buffer (transport->loader,
604                                        &buffer);
605                 
606       orig_len = _dbus_string_get_length (buffer);
607                 
608       _dbus_auth_get_unused_bytes (transport->auth,
609                                    &bytes);
610
611       succeeded = TRUE;
612       if (!_dbus_string_copy (bytes, 0, buffer, _dbus_string_get_length (buffer)))
613         succeeded = FALSE;
614       
615       _dbus_verbose (" %d unused bytes sent to message loader\n", 
616                      _dbus_string_get_length (buffer) -
617                      orig_len);
618       
619       _dbus_message_loader_return_buffer (transport->loader,
620                                           buffer,
621                                           _dbus_string_get_length (buffer) -
622                                           orig_len);
623
624       if (succeeded)
625         _dbus_auth_delete_unused_bytes (transport->auth);
626       else
627         goto nomem;
628     }
629
630   return TRUE;
631
632  nomem:
633   _dbus_verbose ("Not enough memory to transfer unused bytes from auth conversation\n");
634   return FALSE;
635 }
636
637 /**
638  * Reports our current dispatch status (whether there's buffered
639  * data to be queued as messages, or not, or we need memory).
640  *
641  * @param transport the transport
642  * @returns current status
643  */
644 DBusDispatchStatus
645 _dbus_transport_get_dispatch_status (DBusTransport *transport)
646 {
647   if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size)
648     return DBUS_DISPATCH_COMPLETE; /* complete for now */
649
650   if (!_dbus_transport_get_is_authenticated (transport))
651     {
652       switch (_dbus_auth_do_work (transport->auth))
653         {
654         case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
655           return DBUS_DISPATCH_NEED_MEMORY;
656         case DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES:
657           if (!recover_unused_bytes (transport))
658             return DBUS_DISPATCH_NEED_MEMORY;
659           break;
660         default:
661           break;
662         }
663     }
664   
665   if (!_dbus_message_loader_queue_messages (transport->loader))
666     return DBUS_DISPATCH_NEED_MEMORY;
667
668   if (_dbus_message_loader_peek_message (transport->loader) != NULL)
669     return DBUS_DISPATCH_DATA_REMAINS;
670   else
671     return DBUS_DISPATCH_COMPLETE;
672 }
673
674 /**
675  * Processes data we've read while handling a watch, potentially
676  * converting some of it to messages and queueing those messages on
677  * the connection.
678  *
679  * @param transport the transport
680  * @returns #TRUE if we had enough memory to queue all messages
681  */
682 dbus_bool_t
683 _dbus_transport_queue_messages (DBusTransport *transport)
684 {
685   DBusDispatchStatus status;
686   
687   /* Queue any messages */
688   while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS)
689     {
690       DBusMessage *message;
691       DBusList *link;
692
693       link = _dbus_message_loader_pop_message_link (transport->loader);
694       _dbus_assert (link != NULL);
695       
696       message = link->data;
697       
698       _dbus_verbose ("queueing received message %p\n", message);
699
700       _dbus_message_add_size_counter (message, transport->live_messages_size);
701
702       /* pass ownership of link and message ref to connection */
703       _dbus_connection_queue_received_message_link (transport->connection,
704                                                     link);
705     }
706
707   if (_dbus_message_loader_get_is_corrupted (transport->loader))
708     {
709       _dbus_verbose ("Corrupted message stream, disconnecting\n");
710       _dbus_transport_disconnect (transport);
711     }
712
713   return status != DBUS_DISPATCH_NEED_MEMORY;
714 }
715
716 /**
717  * See dbus_connection_set_max_message_size().
718  *
719  * @param transport the transport
720  * @param size the max size of a single message
721  */
722 void
723 _dbus_transport_set_max_message_size (DBusTransport  *transport,
724                                       long            size)
725 {
726   _dbus_message_loader_set_max_message_size (transport->loader, size);
727 }
728
729 /**
730  * See dbus_connection_get_max_message_size().
731  *
732  * @param transport the transport
733  * @returns max message size
734  */
735 long
736 _dbus_transport_get_max_message_size (DBusTransport  *transport)
737 {
738   return _dbus_message_loader_get_max_message_size (transport->loader);
739 }
740
741 /**
742  * See dbus_connection_set_max_live_messages_size().
743  *
744  * @param transport the transport
745  * @param size the max size of all incoming messages
746  */
747 void
748 _dbus_transport_set_max_live_messages_size (DBusTransport  *transport,
749                                             long            size)
750 {
751   transport->max_live_messages_size = size;
752   _dbus_counter_set_notify (transport->live_messages_size,
753                             transport->max_live_messages_size,
754                             live_messages_size_notify,
755                             transport);
756 }
757
758
759 /**
760  * See dbus_connection_get_max_live_messages_size().
761  *
762  * @param transport the transport
763  * @returns max bytes for all live messages
764  */
765 long
766 _dbus_transport_get_max_live_messages_size (DBusTransport  *transport)
767 {
768   return transport->max_live_messages_size;
769 }
770
771 /**
772  * See dbus_connection_get_unix_user().
773  *
774  * @param transport the transport
775  * @param uid return location for the user ID
776  * @returns #TRUE if uid is filled in with a valid user ID
777  */
778 dbus_bool_t
779 _dbus_transport_get_unix_user (DBusTransport *transport,
780                                unsigned long *uid)
781 {
782   DBusCredentials auth_identity;
783
784   *uid = _DBUS_INT_MAX; /* better than some root or system user in
785                          * case of bugs in the caller. Caller should
786                          * never use this value on purpose, however.
787                          */
788   
789   if (!transport->authenticated)
790     return FALSE;
791   
792   _dbus_auth_get_identity (transport->auth, &auth_identity);
793
794   if (auth_identity.uid >= 0)
795     {
796       *uid = auth_identity.uid;
797       return TRUE;
798     }
799   else
800     return FALSE;
801 }
802
803 /**
804  * See dbus_connection_set_unix_user_function().
805  *
806  * @param transport the transport
807  * @param function the predicate
808  * @param data data to pass to the predicate
809  * @param free_data_function function to free the data
810  * @param old_data the old user data to be freed
811  * @param old_free_data_function old free data function to free it with
812  */
813 void
814 _dbus_transport_set_unix_user_function (DBusTransport             *transport,
815                                         DBusAllowUnixUserFunction  function,
816                                         void                      *data,
817                                         DBusFreeFunction           free_data_function,
818                                         void                     **old_data,
819                                         DBusFreeFunction          *old_free_data_function)
820 {  
821   *old_data = transport->unix_user_data;
822   *old_free_data_function = transport->free_unix_user_data;
823
824   transport->unix_user_function = function;
825   transport->unix_user_data = data;
826   transport->free_unix_user_data = free_data_function;
827 }
828
829 /** @} */