2003-04-09 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / connection.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* connection.c  Client connections
3  *
4  * Copyright (C) 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 #include "connection.h"
24 #include "dispatch.h"
25 #include "policy.h"
26 #include "services.h"
27 #include "utils.h"
28 #include <dbus/dbus-list.h>
29
30 static void bus_connection_remove_transactions (DBusConnection *connection);
31
32 struct BusConnections
33 {
34   int refcount;
35   DBusList *list; /**< List of all the connections */
36   BusContext *context;
37 };
38
39 static int connection_data_slot = -1;
40 static int connection_data_slot_refcount = 0;
41
42 typedef struct
43 {
44   BusConnections *connections;
45   DBusConnection *connection;
46   DBusList *services_owned;
47   char *name;
48   DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */
49   DBusMessage *oom_message;
50   DBusPreallocatedSend *oom_preallocated;
51   unsigned long *group_ids;
52   int n_group_ids;
53   BusPolicy *policy;
54 } BusConnectionData;
55
56 #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
57
58 static dbus_bool_t
59 connection_data_slot_ref (void)
60 {
61   if (connection_data_slot < 0)
62     {
63       connection_data_slot = dbus_connection_allocate_data_slot ();
64       
65       if (connection_data_slot < 0)
66         return FALSE;
67
68       _dbus_assert (connection_data_slot_refcount == 0);
69     }  
70
71   connection_data_slot_refcount += 1;
72
73   return TRUE;
74
75 }
76
77 static void
78 connection_data_slot_unref (void)
79 {
80   _dbus_assert (connection_data_slot_refcount > 0);
81
82   connection_data_slot_refcount -= 1;
83   
84   if (connection_data_slot_refcount == 0)
85     {
86       dbus_connection_free_data_slot (connection_data_slot);
87       connection_data_slot = -1;
88     }
89 }
90
91 static DBusLoop*
92 connection_get_loop (DBusConnection *connection)
93 {
94   BusConnectionData *d;
95
96   d = BUS_CONNECTION_DATA (connection);
97
98   return bus_context_get_loop (d->connections->context);
99 }
100
101 void
102 bus_connection_disconnected (DBusConnection *connection)
103 {
104   BusConnectionData *d;
105   BusService *service;
106
107   d = BUS_CONNECTION_DATA (connection);
108   _dbus_assert (d != NULL);
109
110   _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n",
111                  d->name ? d->name : "(inactive)");
112   
113   /* Drop any service ownership. FIXME Unfortunately, this requires
114    * memory allocation and there doesn't seem to be a good way to
115    * handle it other than sleeping; we can't "fail" the operation of
116    * disconnecting a client, and preallocating a broadcast "service is
117    * now gone" message for every client-service pair seems kind of
118    * involved. Probably we need to do that though, and also
119    * extend BusTransaction to be able to revert generic
120    * stuff, not just sending a message (so we can e.g. revert
121    * removal of service owners).
122    */
123   while ((service = _dbus_list_get_last (&d->services_owned)))
124     {
125       BusTransaction *transaction;
126       DBusError error;
127
128     retry:
129       
130       dbus_error_init (&error);
131         
132       transaction = NULL;
133       while (transaction == NULL)
134         {
135           transaction = bus_transaction_new (d->connections->context);
136           _dbus_wait_for_memory ();
137         }
138         
139       if (!bus_service_remove_owner (service, connection,
140                                      transaction, &error))
141         {
142           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
143             {
144               dbus_error_free (&error);
145               bus_transaction_cancel_and_free (transaction);
146               _dbus_wait_for_memory ();
147               goto retry;
148             }
149           else
150             _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
151         }
152         
153       bus_transaction_execute_and_free (transaction);
154     }
155
156   bus_dispatch_remove_connection (connection);
157   
158   /* no more watching */
159   if (!dbus_connection_set_watch_functions (connection,
160                                             NULL, NULL, NULL,
161                                             connection,
162                                             NULL))
163     _dbus_assert_not_reached ("setting watch functions to NULL failed");
164
165   if (!dbus_connection_set_timeout_functions (connection,
166                                               NULL, NULL, NULL,
167                                               connection,
168                                               NULL))
169     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
170   
171   dbus_connection_set_unix_user_function (connection,
172                                           NULL, NULL, NULL);
173
174   dbus_connection_set_dispatch_status_function (connection,
175                                                 NULL, NULL, NULL);
176   
177   bus_connection_remove_transactions (connection);
178
179   _dbus_list_remove (&d->connections->list, connection);
180
181   /* frees "d" as side effect */
182   dbus_connection_set_data (connection,
183                             connection_data_slot,
184                             NULL, NULL);
185
186   dbus_connection_unref (connection);
187 }
188
189 static dbus_bool_t
190 connection_watch_callback (DBusWatch     *watch,
191                            unsigned int   condition,
192                            void          *data)
193 {
194   DBusConnection *connection = data;
195   dbus_bool_t retval;
196   
197   retval = dbus_connection_handle_watch (connection, watch, condition);
198
199   return retval;
200 }
201
202 static dbus_bool_t
203 add_connection_watch (DBusWatch      *watch,
204                       void           *data)
205 {
206   DBusConnection *connection = data;
207
208   return _dbus_loop_add_watch (connection_get_loop (connection),
209                                watch, connection_watch_callback, connection,
210                                NULL);
211 }
212
213 static void
214 remove_connection_watch (DBusWatch      *watch,
215                          void           *data)
216 {
217   DBusConnection *connection = data;
218   
219   _dbus_loop_remove_watch (connection_get_loop (connection),
220                            watch, connection_watch_callback, connection);
221 }
222
223 static void
224 connection_timeout_callback (DBusTimeout   *timeout,
225                              void          *data)
226 {
227   DBusConnection *connection = data;
228
229   /* can return FALSE on OOM but we just let it fire again later */
230   dbus_timeout_handle (timeout);
231 }
232
233 static dbus_bool_t
234 add_connection_timeout (DBusTimeout    *timeout,
235                         void           *data)
236 {
237   DBusConnection *connection = data;
238   
239   return _dbus_loop_add_timeout (connection_get_loop (connection),
240                                  timeout, connection_timeout_callback, connection, NULL);
241 }
242
243 static void
244 remove_connection_timeout (DBusTimeout    *timeout,
245                            void           *data)
246 {
247   DBusConnection *connection = data;
248   
249   _dbus_loop_remove_timeout (connection_get_loop (connection),
250                              timeout, connection_timeout_callback, connection);
251 }
252
253 static void
254 dispatch_status_function (DBusConnection    *connection,
255                           DBusDispatchStatus new_status,
256                           void              *data)
257 {
258   DBusLoop *loop = data;
259   
260   if (new_status != DBUS_DISPATCH_COMPLETE)
261     {
262       while (!_dbus_loop_queue_dispatch (loop, connection))
263         _dbus_wait_for_memory ();
264     }
265 }
266
267 static dbus_bool_t
268 allow_user_function (DBusConnection *connection,
269                      unsigned long   uid,
270                      void           *data)
271 {
272   BusConnectionData *d;
273     
274   d = BUS_CONNECTION_DATA (connection);
275
276   _dbus_assert (d != NULL);
277
278   return TRUE; /* FIXME - this is just until we can parse a config file */
279   
280   return bus_context_allow_user (d->connections->context, uid);
281 }
282
283 static void
284 free_connection_data (void *data)
285 {
286   BusConnectionData *d = data;
287
288   /* services_owned should be NULL since we should be disconnected */
289   _dbus_assert (d->services_owned == NULL);
290   /* similarly */
291   _dbus_assert (d->transaction_messages == NULL);
292
293   if (d->oom_preallocated)
294     dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
295
296   if (d->oom_message)
297     dbus_message_unref (d->oom_message);
298
299   if (d->policy)
300     bus_policy_unref (d->policy);
301   
302   dbus_free (d->group_ids);
303   
304   dbus_free (d->name);
305   
306   dbus_free (d);
307 }
308
309 BusConnections*
310 bus_connections_new (BusContext *context)
311 {
312   BusConnections *connections;
313
314   if (!connection_data_slot_ref ())
315     return NULL;
316
317   connections = dbus_new0 (BusConnections, 1);
318   if (connections == NULL)
319     {
320       connection_data_slot_unref ();
321       return NULL;
322     }
323   
324   connections->refcount = 1;
325   connections->context = context;
326   
327   return connections;
328 }
329
330 void
331 bus_connections_ref (BusConnections *connections)
332 {
333   _dbus_assert (connections->refcount > 0);
334   connections->refcount += 1;
335 }
336
337 void
338 bus_connections_unref (BusConnections *connections)
339 {
340   _dbus_assert (connections->refcount > 0);
341   connections->refcount -= 1;
342   if (connections->refcount == 0)
343     {
344       while (connections->list != NULL)
345         {
346           DBusConnection *connection;
347
348           connection = connections->list->data;
349
350           dbus_connection_ref (connection);
351           dbus_connection_disconnect (connection);
352           bus_connection_disconnected (connection);
353           dbus_connection_unref (connection);
354         }
355       
356       _dbus_list_clear (&connections->list);
357       
358       dbus_free (connections);
359
360       connection_data_slot_unref ();
361     }
362 }
363
364 dbus_bool_t
365 bus_connections_setup_connection (BusConnections *connections,
366                                   DBusConnection *connection)
367 {
368   BusConnectionData *d;
369   dbus_bool_t retval;
370   
371   d = dbus_new0 (BusConnectionData, 1);
372   
373   if (d == NULL)
374     return FALSE;
375
376   d->connections = connections;
377   d->connection = connection;
378
379   _dbus_assert (connection_data_slot >= 0);
380   
381   if (!dbus_connection_set_data (connection,
382                                  connection_data_slot,
383                                  d, free_connection_data))
384     {
385       dbus_free (d);
386       return FALSE;
387     }
388
389   retval = FALSE;
390
391   d->n_group_ids = 0;
392   d->group_ids = NULL;
393   
394   if (!dbus_connection_set_watch_functions (connection,
395                                             add_connection_watch,
396                                             remove_connection_watch,
397                                             NULL,
398                                             connection,
399                                             NULL))
400     goto out;
401   
402   if (!dbus_connection_set_timeout_functions (connection,
403                                               add_connection_timeout,
404                                               remove_connection_timeout,
405                                               NULL,
406                                               connection, NULL))
407     goto out;
408
409
410   dbus_connection_set_unix_user_function (connection,
411                                           allow_user_function,
412                                           NULL, NULL);
413
414   dbus_connection_set_dispatch_status_function (connection,
415                                                 dispatch_status_function,
416                                                 bus_context_get_loop (connections->context),
417                                                 NULL);
418   
419   /* Setup the connection with the dispatcher */
420   if (!bus_dispatch_add_connection (connection))
421     goto out;
422   
423   if (!_dbus_list_append (&connections->list, connection))
424     {
425       bus_dispatch_remove_connection (connection);
426       goto out;
427     }
428
429   if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
430     {
431       if (!_dbus_loop_queue_dispatch (bus_context_get_loop (connections->context), connection))
432         {
433           bus_dispatch_remove_connection (connection);
434           goto out;
435         }
436     }
437   
438   dbus_connection_ref (connection);
439   retval = TRUE;
440
441  out:
442   if (!retval)
443     {        
444       if (!dbus_connection_set_watch_functions (connection,
445                                                 NULL, NULL, NULL,
446                                                 connection,
447                                                 NULL))
448         _dbus_assert_not_reached ("setting watch functions to NULL failed");
449       
450       if (!dbus_connection_set_timeout_functions (connection,
451                                                   NULL, NULL, NULL,
452                                                   connection,
453                                                   NULL))
454         _dbus_assert_not_reached ("setting timeout functions to NULL failed");
455
456       dbus_connection_set_unix_user_function (connection,
457                                               NULL, NULL, NULL);
458
459       dbus_connection_set_dispatch_status_function (connection,
460                                                     NULL, NULL, NULL);
461       
462       if (!dbus_connection_set_data (connection,
463                                      connection_data_slot,
464                                      NULL, NULL))
465         _dbus_assert_not_reached ("failed to set connection data to null");
466     }
467   
468   return retval;
469 }
470
471 dbus_bool_t
472 bus_connection_get_groups  (DBusConnection       *connection,
473                             const unsigned long **groups,
474                             int                  *n_groups)
475 {
476   BusConnectionData *d;
477     
478   d = BUS_CONNECTION_DATA (connection);
479
480   _dbus_assert (d != NULL);
481
482   *groups = NULL;
483   *n_groups = 0;
484
485   /* we do a lazy lookup on groups a user is in for two reasons:
486    * 1) we can't do it on connection setup since the user
487    * hasn't authenticated and 2) it might be expensive
488    * and we don't need to do it if there are no group-based
489    * rules in the config file
490    */
491   
492   if (d->n_group_ids == 0)
493     {
494       unsigned long uid;
495       
496       if (dbus_connection_get_unix_user (connection, &uid))
497         {
498           if (!_dbus_get_groups (uid, &d->group_ids, &d->n_group_ids))
499             {
500               _dbus_verbose ("Did not get any groups for UID %lu\n",
501                              uid);
502               return FALSE;
503             }
504         }
505     }
506
507   *groups = d->group_ids;
508   *n_groups = d->n_group_ids;
509
510   return TRUE;
511 }
512
513 dbus_bool_t
514 bus_connection_is_in_group (DBusConnection *connection,
515                             unsigned long   gid)
516 {
517   int i;
518   const unsigned long *group_ids;
519   int n_group_ids;
520
521   if (!bus_connection_get_groups (connection, &group_ids, &n_group_ids))
522     return FALSE;
523
524   i = 0;
525   while (i < n_group_ids)
526     {
527       if (group_ids[i] == gid)
528         return TRUE;
529       ++i;
530     }
531
532   return FALSE;
533 }
534
535 BusPolicy*
536 bus_connection_get_policy (DBusConnection *connection)
537 {
538   BusConnectionData *d;
539     
540   d = BUS_CONNECTION_DATA (connection);
541
542   _dbus_assert (d != NULL);
543
544   if (!dbus_connection_get_is_authenticated (connection))
545     {
546       _dbus_verbose ("Tried to get policy for unauthenticated connection!\n");
547       return NULL;
548     }
549   
550   /* We do lazy creation of the policy because
551    * it can only be done post-authentication.
552    */
553   if (d->policy == NULL)
554     {
555       d->policy =
556         bus_context_create_connection_policy (d->connections->context,
557                                               connection);
558
559       /* we may have a NULL policy on OOM or error getting list of
560        * groups for a user. In the latter case we don't handle it so
561        * well currently, just keep pretending we're out of memory,
562        * which is kind of bizarre.
563        */
564     }
565
566   return d->policy;
567 }
568
569 /**
570  * Calls function on each connection; if the function returns
571  * #FALSE, stops iterating.
572  *
573  * @param connections the connections object
574  * @param function the function
575  * @param data data to pass to it as a second arg
576  */
577 void
578 bus_connections_foreach (BusConnections               *connections,
579                          BusConnectionForeachFunction  function,
580                         void                          *data)
581 {
582   DBusList *link;
583   
584   link = _dbus_list_get_first_link (&connections->list);
585   while (link != NULL)
586     {
587       DBusConnection *connection = link->data;
588       DBusList *next = _dbus_list_get_next_link (&connections->list, link);
589
590       if (!(* function) (connection, data))
591         break;
592       
593       link = next;
594     }
595 }
596
597 BusContext*
598 bus_connections_get_context (BusConnections *connections)
599 {
600   return connections->context;
601 }
602
603 BusContext*
604 bus_connection_get_context (DBusConnection *connection)
605 {
606   BusConnectionData *d;
607
608   d = BUS_CONNECTION_DATA (connection);
609
610   _dbus_assert (d != NULL);
611
612   return d->connections->context;
613 }
614
615 BusConnections*
616 bus_connection_get_connections (DBusConnection *connection)
617 {
618   BusConnectionData *d;
619     
620   d = BUS_CONNECTION_DATA (connection);
621
622   _dbus_assert (d != NULL);
623
624   return d->connections;
625 }
626
627 BusRegistry*
628 bus_connection_get_registry (DBusConnection *connection)
629 {
630   BusConnectionData *d;
631
632   d = BUS_CONNECTION_DATA (connection);
633
634   _dbus_assert (d != NULL);
635
636   return bus_context_get_registry (d->connections->context);
637 }
638
639 BusActivation*
640 bus_connection_get_activation (DBusConnection *connection)
641 {
642   BusConnectionData *d;
643
644   d = BUS_CONNECTION_DATA (connection);
645
646   _dbus_assert (d != NULL);
647
648   return bus_context_get_activation (d->connections->context);
649 }
650
651 /**
652  * Checks whether the connection is registered with the message bus.
653  *
654  * @param connection the connection
655  * @returns #TRUE if we're an active message bus participant
656  */
657 dbus_bool_t
658 bus_connection_is_active (DBusConnection *connection)
659 {
660   BusConnectionData *d;
661
662   d = BUS_CONNECTION_DATA (connection);
663   
664   return d != NULL && d->name != NULL;
665 }
666
667 dbus_bool_t
668 bus_connection_preallocate_oom_error (DBusConnection *connection)
669 {
670   DBusMessage *message;
671   DBusPreallocatedSend *preallocated;
672   BusConnectionData *d;
673
674   d = BUS_CONNECTION_DATA (connection);  
675
676   _dbus_assert (d != NULL);
677
678   if (d->oom_preallocated != NULL)
679     return TRUE;
680   
681   preallocated = dbus_connection_preallocate_send (connection);
682   if (preallocated == NULL)
683     return FALSE;
684
685   /* d->name may be NULL, but that should be OK */
686   message = dbus_message_new (d->name,
687                               DBUS_ERROR_NO_MEMORY);
688   if (message == NULL)
689     {
690       dbus_connection_free_preallocated_send (connection, preallocated);
691       return FALSE;
692     }
693
694   dbus_message_set_is_error (message, TRUE);
695
696   if (!dbus_message_set_sender (message,
697                                 DBUS_SERVICE_DBUS))
698     {
699       dbus_connection_free_preallocated_send (connection, preallocated);
700       dbus_message_unref (message);
701       return FALSE;
702     }
703   
704   /* set reply serial to placeholder value just so space is already allocated
705    * for it.
706    */
707   if (!dbus_message_set_reply_serial (message, 14))
708     {
709       dbus_connection_free_preallocated_send (connection, preallocated);
710       dbus_message_unref (message);
711       return FALSE;
712     }
713
714   d->oom_message = message;
715   d->oom_preallocated = preallocated;
716   
717   return TRUE;
718 }
719
720 void
721 bus_connection_send_oom_error (DBusConnection *connection,
722                                DBusMessage    *in_reply_to)
723 {
724   BusConnectionData *d;
725
726   d = BUS_CONNECTION_DATA (connection);  
727
728   _dbus_assert (d != NULL);  
729   _dbus_assert (d->oom_message != NULL);
730
731   /* should always succeed since we set it to a placeholder earlier */
732   if (!dbus_message_set_reply_serial (d->oom_message,
733                                       dbus_message_get_serial (in_reply_to)))
734     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
735
736   _dbus_assert (dbus_message_get_sender (d->oom_message) != NULL);
737   
738   dbus_connection_send_preallocated (connection, d->oom_preallocated,
739                                      d->oom_message, NULL);
740
741   dbus_message_unref (d->oom_message);
742   d->oom_message = NULL;
743   d->oom_preallocated = NULL;
744 }
745
746 dbus_bool_t
747 bus_connection_add_owned_service (DBusConnection *connection,
748                                   BusService     *service)
749 {
750   BusConnectionData *d;
751
752   d = BUS_CONNECTION_DATA (connection);
753   _dbus_assert (d != NULL);
754
755   if (!_dbus_list_append (&d->services_owned,
756                           service))
757     return FALSE;
758
759   return TRUE;
760 }
761
762 void
763 bus_connection_remove_owned_service (DBusConnection *connection,
764                                      BusService     *service)
765 {
766   BusConnectionData *d;
767
768   d = BUS_CONNECTION_DATA (connection);
769   _dbus_assert (d != NULL);
770
771   _dbus_list_remove_last (&d->services_owned, service);
772 }
773
774 dbus_bool_t
775 bus_connection_set_name (DBusConnection   *connection,
776                          const DBusString *name)
777 {
778   BusConnectionData *d;
779   
780   d = BUS_CONNECTION_DATA (connection);
781   _dbus_assert (d != NULL);
782   _dbus_assert (d->name == NULL);
783
784   if (!_dbus_string_copy_data (name, &d->name))
785     return FALSE;
786
787   _dbus_assert (d->name != NULL);
788   
789   _dbus_verbose ("Name %s assigned to %p\n", d->name, connection);
790   
791   return TRUE;
792 }
793
794 const char *
795 bus_connection_get_name (DBusConnection *connection)
796 {
797   BusConnectionData *d;
798   
799   d = BUS_CONNECTION_DATA (connection);
800   _dbus_assert (d != NULL);
801   
802   return d->name;
803 }
804
805 typedef struct
806 {
807   BusTransaction *transaction;
808   DBusMessage    *message;
809   DBusPreallocatedSend *preallocated;
810 } MessageToSend;
811
812 struct BusTransaction
813 {
814   DBusList *connections;
815   BusContext *context;
816 };
817
818 static void
819 message_to_send_free (DBusConnection *connection,
820                       MessageToSend  *to_send)
821 {
822   if (to_send->message)
823     dbus_message_unref (to_send->message);
824
825   if (to_send->preallocated)
826     dbus_connection_free_preallocated_send (connection, to_send->preallocated);
827
828   dbus_free (to_send);
829 }
830
831 BusTransaction*
832 bus_transaction_new (BusContext *context)
833 {
834   BusTransaction *transaction;
835
836   transaction = dbus_new0 (BusTransaction, 1);
837   if (transaction == NULL)
838     return NULL;
839
840   transaction->context = context;
841   
842   return transaction;
843 }
844
845 BusContext*
846 bus_transaction_get_context (BusTransaction  *transaction)
847 {
848   return transaction->context;
849 }
850
851 BusConnections*
852 bus_transaction_get_connections (BusTransaction  *transaction)
853 {
854   return bus_context_get_connections (transaction->context);
855 }
856
857 dbus_bool_t
858 bus_transaction_send_message (BusTransaction *transaction,
859                               DBusConnection *connection,
860                               DBusMessage    *message)
861 {
862   MessageToSend *to_send;
863   BusConnectionData *d;
864   DBusList *link;
865
866   _dbus_verbose ("  trying to add message %s to transaction%s\n",
867                  dbus_message_get_name (message),
868                  dbus_connection_get_is_connected (connection) ?
869                  "" : " (disconnected)");
870
871   _dbus_assert (dbus_message_get_sender (message) != NULL);
872   
873   if (!dbus_connection_get_is_connected (connection))
874     return TRUE; /* silently ignore disconnected connections */
875   
876   d = BUS_CONNECTION_DATA (connection);
877   _dbus_assert (d != NULL);
878   
879   to_send = dbus_new (MessageToSend, 1);
880   if (to_send == NULL)
881     {
882       return FALSE;
883     }
884
885   to_send->preallocated = dbus_connection_preallocate_send (connection);
886   if (to_send->preallocated == NULL)
887     {
888       dbus_free (to_send);
889       return FALSE;
890     }  
891   
892   dbus_message_ref (message);
893   to_send->message = message;
894   to_send->transaction = transaction;
895
896   _dbus_verbose ("about to prepend message\n");
897   
898   if (!_dbus_list_prepend (&d->transaction_messages, to_send))
899     {
900       message_to_send_free (connection, to_send);
901       return FALSE;
902     }
903
904   _dbus_verbose ("prepended message\n");
905   
906   /* See if we already had this connection in the list
907    * for this transaction. If we have a pending message,
908    * then we should already be in transaction->connections
909    */
910   link = _dbus_list_get_first_link (&d->transaction_messages);
911   _dbus_assert (link->data == to_send);
912   link = _dbus_list_get_next_link (&d->transaction_messages, link);
913   while (link != NULL)
914     {
915       MessageToSend *m = link->data;
916       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
917       
918       if (m->transaction == transaction)
919         break;
920         
921       link = next;
922     }
923
924   if (link == NULL)
925     {
926       if (!_dbus_list_prepend (&transaction->connections, connection))
927         {
928           _dbus_list_remove (&d->transaction_messages, to_send);
929           message_to_send_free (connection, to_send);
930           return FALSE;
931         }
932     }
933
934   return TRUE;
935 }
936
937 static void
938 connection_cancel_transaction (DBusConnection *connection,
939                                BusTransaction *transaction)
940 {
941   DBusList *link;
942   BusConnectionData *d;
943   
944   d = BUS_CONNECTION_DATA (connection);
945   _dbus_assert (d != NULL);
946   
947   link = _dbus_list_get_first_link (&d->transaction_messages);
948   while (link != NULL)
949     {
950       MessageToSend *m = link->data;
951       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
952       
953       if (m->transaction == transaction)
954         {
955           _dbus_list_remove_link (&d->transaction_messages,
956                                   link);
957           
958           message_to_send_free (connection, m);
959         }
960         
961       link = next;
962     }
963 }
964
965 void
966 bus_transaction_cancel_and_free (BusTransaction *transaction)
967 {
968   DBusConnection *connection;
969
970   _dbus_verbose ("TRANSACTION: cancelled\n");
971   
972   while ((connection = _dbus_list_pop_first (&transaction->connections)))
973     connection_cancel_transaction (connection, transaction);
974
975   _dbus_assert (transaction->connections == NULL);
976
977   dbus_free (transaction);
978 }
979
980 static void
981 connection_execute_transaction (DBusConnection *connection,
982                                 BusTransaction *transaction)
983 {
984   DBusList *link;
985   BusConnectionData *d;
986   
987   d = BUS_CONNECTION_DATA (connection);
988   _dbus_assert (d != NULL);
989
990   /* Send the queue in order (FIFO) */
991   link = _dbus_list_get_last_link (&d->transaction_messages);
992   while (link != NULL)
993     {
994       MessageToSend *m = link->data;
995       DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
996       
997       if (m->transaction == transaction)
998         {
999           _dbus_list_remove_link (&d->transaction_messages,
1000                                   link);
1001
1002           _dbus_assert (dbus_message_get_sender (m->message) != NULL);
1003           
1004           dbus_connection_send_preallocated (connection,
1005                                              m->preallocated,
1006                                              m->message,
1007                                              NULL);
1008
1009           m->preallocated = NULL; /* so we don't double-free it */
1010           
1011           message_to_send_free (connection, m);
1012         }
1013         
1014       link = prev;
1015     }
1016 }
1017
1018 void
1019 bus_transaction_execute_and_free (BusTransaction *transaction)
1020 {
1021   /* For each connection in transaction->connections
1022    * send the messages
1023    */
1024   DBusConnection *connection;
1025
1026   _dbus_verbose ("TRANSACTION: executing\n");
1027   
1028   while ((connection = _dbus_list_pop_first (&transaction->connections)))
1029     connection_execute_transaction (connection, transaction);
1030
1031   _dbus_assert (transaction->connections == NULL);
1032
1033   dbus_free (transaction);
1034 }
1035
1036 static void
1037 bus_connection_remove_transactions (DBusConnection *connection)
1038 {
1039   MessageToSend *to_send;
1040   BusConnectionData *d;
1041   
1042   d = BUS_CONNECTION_DATA (connection);
1043   _dbus_assert (d != NULL);
1044   
1045   while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
1046     {
1047       /* only has an effect for the first MessageToSend listing this transaction */
1048       _dbus_list_remove (&to_send->transaction->connections,
1049                          connection);
1050
1051       _dbus_list_remove (&d->transaction_messages, to_send);
1052       message_to_send_free (connection, to_send);
1053     }
1054 }
1055
1056 /**
1057  * Converts the DBusError to a message reply
1058  */
1059 dbus_bool_t
1060 bus_transaction_send_error_reply (BusTransaction  *transaction,
1061                                   DBusConnection  *connection,
1062                                   const DBusError *error,
1063                                   DBusMessage     *in_reply_to)
1064 {
1065   DBusMessage *reply;
1066   
1067   _dbus_assert (error != NULL);
1068   _DBUS_ASSERT_ERROR_IS_SET (error);
1069   
1070   reply = dbus_message_new_error_reply (in_reply_to,
1071                                         error->name,
1072                                         error->message);
1073   if (reply == NULL)
1074     return FALSE;
1075
1076   if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS) ||
1077       !bus_transaction_send_message (transaction, connection, reply))
1078     {
1079       dbus_message_unref (reply);
1080       return FALSE;
1081     }
1082
1083   dbus_message_unref (reply);
1084   
1085   return TRUE;
1086 }