2003-03-31 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-address.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-address.c  Server address parser.
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 <config.h>
25 #include "dbus-address.h"
26 #include "dbus-internals.h"
27 #include "dbus-list.h"
28
29 /**
30  * @defgroup DBusAddress Address parsing
31  * @ingroup  DBus
32  * @brief Parsing addresses of D-BUS servers.
33  *
34  * @{
35  */
36 struct DBusAddressEntry
37 {
38   DBusString method;
39
40   DBusList *keys;
41   DBusList *values;
42 };
43
44 static void
45 dbus_address_entry_free (DBusAddressEntry *entry)
46 {
47   DBusList *link;
48   
49   _dbus_string_free (&entry->method);
50
51   link = _dbus_list_get_first_link (&entry->keys);
52   while (link != NULL)
53     {
54       _dbus_string_free (link->data);
55       dbus_free (link->data);
56       
57       link = _dbus_list_get_next_link (&entry->keys, link);
58     }
59   _dbus_list_clear (&entry->keys);
60   
61   link = _dbus_list_get_first_link (&entry->values);
62   while (link != NULL)
63     {
64       _dbus_string_free (link->data);
65       dbus_free (link->data);
66       
67       link = _dbus_list_get_next_link (&entry->values, link);
68     }
69   _dbus_list_clear (&entry->values);
70   
71   dbus_free (entry);
72 }
73
74
75 /**
76  * Frees a #NULL-terminated array of address entries.
77  *
78  * @param entries the array.
79  */
80 void
81 dbus_address_entries_free (DBusAddressEntry **entries)
82 {
83   int i;
84   
85   for (i = 0; entries[i] != NULL; i++)
86     dbus_address_entry_free (entries[i]);
87   dbus_free (entries);
88 }
89
90 static DBusAddressEntry *
91 create_entry (void)
92 {
93   DBusAddressEntry *entry;
94
95   entry = dbus_new0 (DBusAddressEntry, 1);
96
97   if (entry == NULL)
98     return NULL;
99
100   if (!_dbus_string_init (&entry->method))
101     {
102       dbus_free (entry);
103       return NULL;
104     }
105
106   return entry;
107 }
108
109 /**
110  * Returns the method string of an address entry.
111  *
112  * @param entry the entry.
113  * @returns a string describing the method. This string
114  * must not be freed.
115  */
116 const char *
117 dbus_address_entry_get_method (DBusAddressEntry *entry)
118 {
119   return _dbus_string_get_const_data (&entry->method);
120 }
121
122 /**
123  * Returns a value from a key of an entry.
124  *
125  * @param entry the entry.
126  * @param key the key.
127  * @returns the key value. This string must not be fred.
128  */
129 const char *
130 dbus_address_entry_get_value (DBusAddressEntry *entry,
131                               const char       *key)
132 {
133   DBusList *values, *keys;
134
135   keys = _dbus_list_get_first_link (&entry->keys);
136   values = _dbus_list_get_first_link (&entry->values);
137
138   while (keys != NULL)
139     {
140       _dbus_assert (values != NULL);
141
142       if (_dbus_string_equal_c_str (keys->data, key))
143         return _dbus_string_get_const_data (values->data);
144
145       keys = _dbus_list_get_next_link (&entry->keys, keys);
146       values = _dbus_list_get_next_link (&entry->values, values);
147     }
148   
149   return NULL;
150 }
151
152 /**
153  * Parses an address string of the form:
154  *
155  * method:key=value,key=value;method:key=value
156  *
157  * @todo document address format in the specification
158  *
159  * @todo need to be able to escape ';' and ',' in the
160  * key values, and the parsing needs to handle that.
161  * 
162  * @param address the address.
163  * @param entry return location to an array of entries.
164  * @param array_len return location for array length.
165  * @param error address where an error can be returned.
166  * @returns #TRUE on success, #FALSE otherwise.
167  */
168 dbus_bool_t
169 dbus_parse_address (const char         *address,
170                     DBusAddressEntry ***entry,
171                     int                *array_len,
172                     DBusError          *error)
173 {
174   DBusString str;
175   int pos, end_pos, len, i;
176   DBusList *entries, *link;
177   DBusAddressEntry **entry_array;
178
179   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
180   
181   _dbus_string_init_const (&str, address);
182
183   entries = NULL;
184   pos = 0;
185   len = _dbus_string_get_length (&str);
186   
187   while (pos < len)
188     {
189       DBusAddressEntry *entry;
190
191       int found_pos;
192
193       entry = create_entry ();
194       if (!entry)
195         {
196           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
197
198           goto error;
199         }
200       
201       /* Append the entry */
202       if (!_dbus_list_append (&entries, entry))
203         {
204           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
205           dbus_address_entry_free (entry);
206           goto error;
207         }
208       
209       /* Look for a semi-colon */
210       if (!_dbus_string_find (&str, pos, ";", &end_pos))
211         end_pos = len;
212       
213       /* Look for the colon : */
214       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
215         {
216           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
217           goto error;
218         }
219
220       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
221         {
222           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
223           goto error;
224         }
225           
226       pos = found_pos + 1;
227
228       while (pos < end_pos)
229         {
230           int comma_pos, equals_pos;
231
232           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
233             comma_pos = end_pos;
234           
235           if (!_dbus_string_find (&str, pos, "=", &equals_pos) ||
236               equals_pos == pos || equals_pos + 1 == end_pos)
237             {
238               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
239                               "'=' character not found or has no value following it");
240               goto error;
241             }
242           else
243             {
244               DBusString *key;
245               DBusString *value;
246
247               key = dbus_new0 (DBusString, 1);
248
249               if (!key)
250                 {
251                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
252                   goto error;
253                 }
254
255               value = dbus_new0 (DBusString, 1);
256               if (!value)
257                 {
258                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
259                   dbus_free (key);
260                   goto error;
261                 }
262               
263               if (!_dbus_string_init (key))
264                 {
265                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
266                   dbus_free (key);
267                   dbus_free (value);
268                   
269                   goto error;
270                 }
271               
272               if (!_dbus_string_init (value))
273                 {
274                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
275                   _dbus_string_free (key);
276
277                   dbus_free (key);
278                   dbus_free (value);              
279                   goto error;
280                 }
281
282               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
283                 {
284                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
285                   _dbus_string_free (key);
286                   _dbus_string_free (value);
287
288                   dbus_free (key);
289                   dbus_free (value);              
290                   goto error;
291                 }
292
293               if (!_dbus_string_copy_len (&str, equals_pos + 1, comma_pos - equals_pos - 1, value, 0))
294                 {
295                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
296                   _dbus_string_free (key);
297                   _dbus_string_free (value);
298
299                   dbus_free (key);
300                   dbus_free (value);              
301                   goto error;
302                 }
303
304               if (!_dbus_list_append (&entry->keys, key))
305                 {
306                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
307                   _dbus_string_free (key);
308                   _dbus_string_free (value);
309
310                   dbus_free (key);
311                   dbus_free (value);              
312                   goto error;
313                 }
314
315               if (!_dbus_list_append (&entry->values, value))
316                 {
317                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
318                   _dbus_string_free (value);
319
320                   dbus_free (value);
321                   goto error;             
322                 }
323             }
324
325           pos = comma_pos + 1;
326         }
327
328       pos = end_pos + 1;
329     }
330
331   *array_len = _dbus_list_get_length (&entries);
332   
333   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
334
335   if (!entry_array)
336     {
337       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
338       
339       goto error;
340     }
341   
342   entry_array [*array_len] = NULL;
343
344   link = _dbus_list_get_first_link (&entries);
345   i = 0;
346   while (link != NULL)
347     {
348       entry_array[i] = link->data;
349       i++;
350       link = _dbus_list_get_next_link (&entries, link);
351     }
352
353   _dbus_list_clear (&entries);
354   *entry = entry_array;
355
356   return TRUE;
357
358  error:
359   
360   link = _dbus_list_get_first_link (&entries);
361   while (link != NULL)
362     {
363       dbus_address_entry_free (link->data);
364       link = _dbus_list_get_next_link (&entries, link);
365     }
366
367   _dbus_list_clear (&entries);
368   
369   return FALSE;
370   
371 }
372
373
374 /** @} */
375
376 #ifdef DBUS_BUILD_TESTS
377 #include "dbus-test.h"
378
379 dbus_bool_t
380 _dbus_address_test (void)
381 {
382   DBusAddressEntry **entries;
383   int len;  
384   DBusError error;
385
386   dbus_error_init (&error);
387   
388   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
389                            &entries, &len, &error))
390     _dbus_assert_not_reached ("could not parse address");
391   _dbus_assert (len == 2);
392   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
393   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
394   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
395   
396   dbus_address_entries_free (entries);
397
398   /* Different possible errors */
399   if (dbus_parse_address ("foo", &entries, &len, &error))
400     _dbus_assert_not_reached ("Parsed incorrect address.");
401   else
402     dbus_error_free (&error);
403   
404   if (dbus_parse_address ("foo:bar", &entries, &len, &error))
405     _dbus_assert_not_reached ("Parsed incorrect address.");
406   else
407     dbus_error_free (&error);
408   
409   if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
410     _dbus_assert_not_reached ("Parsed incorrect address.");
411   else
412     dbus_error_free (&error);
413   
414   if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
415     _dbus_assert_not_reached ("Parsed incorrect address.");
416   else
417     dbus_error_free (&error);
418   
419   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
420     _dbus_assert_not_reached ("Parsed incorrect address.");
421   else
422     dbus_error_free (&error);
423   
424   if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
425     _dbus_assert_not_reached ("Parsed incorrect address.");
426   else
427     dbus_error_free (&error);
428   
429   if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
430     _dbus_assert_not_reached ("Parsed incorrect address.");
431   else
432     dbus_error_free (&error);
433   
434   return TRUE;
435 }
436
437 #endif