2003-01-26 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / driver.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* driver.c  Bus client (driver)
3  *
4  * Copyright (C) 2003  CodeFactory AB
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 "connection.h"
25 #include "driver.h"
26 #include "dispatch.h"
27 #include "services.h"
28 #include <dbus/dbus-string.h>
29 #include <dbus/dbus-internals.h>
30 #include <string.h>
31
32 static void bus_driver_send_welcome_message (DBusConnection *connection,
33                                              DBusMessage    *hello_message);
34
35 static void
36 bus_driver_send_service_deleted (DBusConnection *connection, const char *name)
37 {
38   DBusMessage *message;
39
40   _dbus_verbose ("sending service deleted: %s\n", name);
41   
42   _DBUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
43                                                 DBUS_MESSAGE_SERVICE_DELETED));
44   
45   _DBUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
46
47   _DBUS_HANDLE_OOM (dbus_message_append_fields (message,
48                                                 DBUS_TYPE_STRING, name,
49                                                 0));
50   bus_dispatch_broadcast_message (message);
51   dbus_message_unref (message);  
52 }
53
54 static void
55 bus_driver_send_service_created (DBusConnection *connection, const char *name)
56 {
57   DBusMessage *message;
58
59   _DBUS_HANDLE_OOM (message = dbus_message_new (DBUS_SERVICE_BROADCAST,
60                                                 DBUS_MESSAGE_SERVICE_CREATED));
61   
62   _DBUS_HANDLE_OOM (dbus_message_set_sender (message, DBUS_SERVICE_DBUS));
63
64   _DBUS_HANDLE_OOM (dbus_message_append_fields (message,
65                                                 DBUS_TYPE_STRING, name,
66                                                 0));
67   bus_dispatch_broadcast_message (message);
68   dbus_message_unref (message);
69 }
70
71 static dbus_bool_t
72 create_unique_client_name (const char *name,
73                            DBusString *str)
74 {
75   /* We never want to use the same unique client name twice, because
76    * we want to guarantee that if you send a message to a given unique
77    * name, you always get the same application. So we use two numbers
78    * for INT_MAX * INT_MAX combinations, should be pretty safe against
79    * wraparound.
80    */
81   static int next_major_number = 0;
82   static int next_minor_number = 0;
83   int len;
84
85   if (!_dbus_string_append (str, name))
86     return FALSE;
87   
88   len = _dbus_string_get_length (str);
89   
90   while (TRUE)
91     {
92       /* start out with 1-0, go to 1-1, 1-2, 1-3,
93        * up to 1-MAXINT, then 2-0, 2-1, etc.
94        */
95       if (next_minor_number <= 0)
96         {
97           next_major_number += 1;
98           next_minor_number = 0;
99           if (next_major_number <= 0)
100             _dbus_assert_not_reached ("INT_MAX * INT_MAX clients were added");
101         }
102
103       _dbus_assert (next_major_number > 0);
104       _dbus_assert (next_minor_number >= 0);
105
106       /* appname:MAJOR-MINOR */
107       
108       if (!_dbus_string_append (str, ":"))
109         return FALSE;
110       
111       if (!_dbus_string_append_int (str, next_major_number))
112         return FALSE;
113
114       if (!_dbus_string_append (str, "-"))
115         return FALSE;
116       
117       if (!_dbus_string_append_int (str, next_minor_number))
118         return FALSE;
119
120       next_minor_number += 1;
121       
122       /* Check if a client with the name exists */
123       if (bus_service_lookup (str, FALSE) == NULL)
124         break;
125
126       /* drop the number again, try the next one. */
127       _dbus_string_set_length (str, len);
128     }
129
130   return TRUE;
131 }
132
133 static void
134 bus_driver_handle_hello (DBusConnection *connection,
135                          DBusMessage    *message)
136 {
137   DBusResultCode result;
138   char *name;
139   DBusString unique_name;
140   BusService *service;
141   
142   _DBUS_HANDLE_OOM ((result = dbus_message_get_fields (message,
143                                                        DBUS_TYPE_STRING, &name,
144                                                        0)) != DBUS_RESULT_NO_MEMORY);
145
146   if (result != DBUS_RESULT_SUCCESS)
147     {
148       dbus_free (name);
149       dbus_connection_disconnect (connection);
150       return;
151     }
152   
153   _DBUS_HANDLE_OOM (_dbus_string_init (&unique_name, _DBUS_INT_MAX));
154
155   _DBUS_HANDLE_OOM (create_unique_client_name (name, &unique_name));
156   
157   dbus_free (name);
158   
159   /* Create the service */
160   _DBUS_HANDLE_OOM (service = bus_service_lookup (&unique_name, TRUE));
161
162   /* Add the connection as the owner */
163   _DBUS_HANDLE_OOM (bus_service_add_owner (service, connection));
164   _DBUS_HANDLE_OOM (bus_connection_set_name (connection, &unique_name));
165
166   _DBUS_HANDLE_OOM (dbus_message_set_sender (message,
167                                              bus_connection_get_name (connection)));
168   
169   _dbus_string_free (&unique_name);
170
171   _DBUS_HANDLE_OOM (bus_driver_send_welcome_message (connection, message));
172
173   /* Broadcast a service created message */
174   bus_driver_send_service_created (connection, bus_service_get_name (service));
175 }
176
177 static void
178 bus_driver_send_welcome_message (DBusConnection *connection,
179                                  DBusMessage    *hello_message)
180 {
181   DBusMessage *welcome;
182   const char *name;
183   
184   name = bus_connection_get_name (connection);
185   _dbus_assert (name != NULL);
186   
187   _DBUS_HANDLE_OOM (welcome = dbus_message_new_reply (DBUS_MESSAGE_WELCOME,
188                                                       hello_message));
189   
190   _DBUS_HANDLE_OOM (dbus_message_set_sender (welcome, DBUS_SERVICE_DBUS));
191   
192   _DBUS_HANDLE_OOM (dbus_message_append_fields (welcome,
193                                                 DBUS_TYPE_STRING, name,
194                                                 NULL));
195   
196   _DBUS_HANDLE_OOM (dbus_connection_send_message (connection, welcome, NULL, NULL));
197
198   dbus_message_unref (welcome);
199 }
200
201 static void
202 bus_driver_handle_list_services (DBusConnection *connection,
203                                  DBusMessage    *message)
204 {
205   DBusMessage *reply;
206   int len, i;
207   char **services;
208
209   _DBUS_HANDLE_OOM (reply = dbus_message_new_reply (DBUS_MESSAGE_SERVICES, message));
210
211   _DBUS_HANDLE_OOM (services = bus_services_list (&len));
212
213   _DBUS_HANDLE_OOM (dbus_message_append_fields (reply,
214                                                 DBUS_TYPE_STRING_ARRAY, services, len,
215                                                 0));
216
217   _DBUS_HANDLE_OOM (dbus_connection_send_message (connection, reply, NULL, NULL));
218
219   dbus_message_unref (reply);
220
221   if (services != NULL)
222     {
223       for (i = 0; i < len; i++)
224         dbus_free (services[i]);
225       dbus_free (services);
226     }
227 }
228
229 void
230 bus_driver_remove_connection (DBusConnection *connection)
231 {
232   BusService *service;
233   DBusString service_name;
234   const char *name;
235
236   name = bus_connection_get_name (connection);
237
238   if (name == NULL)
239     return;
240   
241   _dbus_string_init_const (&service_name, name);
242   
243   service = bus_service_lookup (&service_name, FALSE);
244
245   bus_driver_send_service_deleted (connection, name);
246   
247   if (service)
248     bus_service_free (service);
249 }
250
251 void
252 bus_driver_handle_message (DBusConnection *connection,
253                            DBusMessage    *message)
254 {
255   const char *name, *sender;
256
257   _dbus_verbose ("Driver got a message: %s\n",
258                  dbus_message_get_name (message));
259   
260   name = dbus_message_get_name (message);
261   sender = dbus_message_get_sender (message);
262
263   if (sender == NULL && (strcmp (name, DBUS_MESSAGE_HELLO) != 0))
264     {
265       _dbus_verbose ("Trying to send a message without being registered. Disconnecting.\n");
266       dbus_connection_disconnect (connection);
267       return;
268     }
269
270   /* Now check names. */
271   if (strcmp (name, DBUS_MESSAGE_HELLO) == 0)
272     bus_driver_handle_hello (connection, message);
273   else if (strcmp (name, DBUS_MESSAGE_LIST_SERVICES) == 0)
274     bus_driver_handle_list_services (connection, message);
275 }