2003-03-12 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, _DBUS_INT_MAX))
101     dbus_free (entry);
102
103   return entry;
104 }
105
106 /**
107  * Returns the method string of an address entry.
108  *
109  * @param entry the entry.
110  * @returns a string describing the method. This string
111  * must not be freed.
112  */
113 const char *
114 dbus_address_entry_get_method (DBusAddressEntry *entry)
115 {
116   const char *method;
117
118   _dbus_string_get_const_data (&entry->method, &method);
119
120   return method;
121 }
122
123 /**
124  * Returns a value from a key of an entry.
125  *
126  * @param entry the entry.
127  * @param key the key.
128  * @returns the key value. This string must not be fred.
129  */
130 const char *
131 dbus_address_entry_get_value (DBusAddressEntry *entry,
132                               const char       *key)
133 {
134   DBusList *values, *keys;
135
136   keys = _dbus_list_get_first_link (&entry->keys);
137   values = _dbus_list_get_first_link (&entry->values);
138
139   while (keys != NULL)
140     {
141       _dbus_assert (values != NULL);
142
143       if (_dbus_string_equal_c_str (keys->data, key))
144         {
145           const char *str;
146
147           _dbus_string_get_const_data (values->data, &str);
148           return str;
149         }
150       keys = _dbus_list_get_next_link (&entry->keys, keys);
151       values = _dbus_list_get_next_link (&entry->values, values);
152     }
153   
154   return NULL;
155 }
156
157 /**
158  * Parses an address string of the form:
159  *
160  * method:key=value,key=value;method:key=value
161  *
162  * @todo document address format in the specification
163  *
164  * @todo need to be able to escape ';' and ',' in the
165  * key values, and the parsing needs to handle that.
166  * 
167  * @param address the address.
168  * @param entry return location to an array of entries.
169  * @param array_len return location for array length.
170  * @param result return location for result code.
171  * @returns #TRUE on success, #FALSE otherwise.
172  */
173 dbus_bool_t
174 dbus_parse_address (const char         *address,
175                     DBusAddressEntry ***entry,
176                     int                *array_len,
177                     DBusResultCode     *result)
178 {
179   DBusString str;
180   int pos, end_pos, len, i;
181   DBusList *entries, *link;
182   DBusAddressEntry **entry_array;
183
184   _dbus_string_init_const (&str, address);
185
186   entries = NULL;
187   pos = 0;
188   len = _dbus_string_get_length (&str);
189   
190   while (pos < len)
191     {
192       DBusAddressEntry *entry;
193
194       int found_pos;
195
196       entry = create_entry ();
197       if (!entry)
198         {
199           dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
200
201           goto error;
202         }
203       
204       /* Append the entry */
205       if (!_dbus_list_append (&entries, entry))
206         {
207           dbus_set_result (result, DBUS_RESULT_NO_MEMORY);        
208           dbus_address_entry_free (entry);
209           goto error;
210         }
211       
212       /* Look for a semi-colon */
213       if (!_dbus_string_find (&str, pos, ";", &end_pos))
214         end_pos = len;
215       
216       /* Look for the colon : */
217       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
218         {
219           dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
220           goto error;
221         }
222
223       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
224         {
225           dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
226           goto error;
227         }
228           
229       pos = found_pos + 1;
230
231       while (pos < end_pos)
232         {
233           int comma_pos, equals_pos;
234
235           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
236             comma_pos = end_pos;
237           
238           if (!_dbus_string_find (&str, pos, "=", &equals_pos) ||
239               equals_pos == pos || equals_pos + 1 == end_pos)
240             {
241               dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS);
242               goto error;
243             }
244           else
245             {
246               DBusString *key;
247               DBusString *value;
248
249               key = dbus_new0 (DBusString, 1);
250
251               if (!key)
252                 {
253                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);                
254                   goto error;
255                 }
256
257               value = dbus_new0 (DBusString, 1);
258               if (!value)
259                 {
260                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
261                   dbus_free (key);
262                   goto error;
263                 }
264               
265               if (!_dbus_string_init (key, _DBUS_INT_MAX))
266                 {
267                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
268                   dbus_free (key);
269                   dbus_free (value);
270                   
271                   goto error;
272                 }
273               
274               if (!_dbus_string_init (value, _DBUS_INT_MAX))
275                 {
276                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
277                   _dbus_string_free (key);
278
279                   dbus_free (key);
280                   dbus_free (value);              
281                   goto error;
282                 }
283
284               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
285                 {
286                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
287                   _dbus_string_free (key);
288                   _dbus_string_free (value);
289
290                   dbus_free (key);
291                   dbus_free (value);              
292                   goto error;
293                 }
294
295               if (!_dbus_string_copy_len (&str, equals_pos + 1, comma_pos - equals_pos - 1, value, 0))
296                 {
297                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);                
298                   _dbus_string_free (key);
299                   _dbus_string_free (value);
300
301                   dbus_free (key);
302                   dbus_free (value);              
303                   goto error;
304                 }
305
306               if (!_dbus_list_append (&entry->keys, key))
307                 {
308                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);                
309                   _dbus_string_free (key);
310                   _dbus_string_free (value);
311
312                   dbus_free (key);
313                   dbus_free (value);              
314                   goto error;
315                 }
316
317               if (!_dbus_list_append (&entry->values, value))
318                 {
319                   dbus_set_result (result, DBUS_RESULT_NO_MEMORY);                
320                   _dbus_string_free (value);
321
322                   dbus_free (value);
323                   goto error;             
324                 }
325             }
326
327           pos = comma_pos + 1;
328         }
329
330       pos = end_pos + 1;
331     }
332
333   *array_len = _dbus_list_get_length (&entries);
334   
335   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
336
337   if (!entry_array)
338     {
339       dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
340       
341       goto error;
342     }
343   
344   entry_array [*array_len] = NULL;
345
346   link = _dbus_list_get_first_link (&entries);
347   i = 0;
348   while (link != NULL)
349     {
350       entry_array[i] = link->data;
351       i++;
352       link = _dbus_list_get_next_link (&entries, link);
353     }
354
355   _dbus_list_clear (&entries);
356   *entry = entry_array;
357
358   dbus_set_result (result, DBUS_RESULT_SUCCESS);
359   return TRUE;
360
361  error:
362   
363   link = _dbus_list_get_first_link (&entries);
364   while (link != NULL)
365     {
366       dbus_address_entry_free (link->data);
367       link = _dbus_list_get_next_link (&entries, link);
368     }
369
370   return FALSE;
371   
372 }
373
374
375 /** @} */
376
377 #ifdef DBUS_BUILD_TESTS
378 #include "dbus-test.h"
379
380 dbus_bool_t
381 _dbus_address_test (void)
382 {
383   DBusAddressEntry **entries;
384   int len;  
385   DBusResultCode result;
386
387   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
388                            &entries, &len, &result))
389     _dbus_assert_not_reached ("could not parse address");
390   _dbus_assert (len == 2);
391   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
392   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
393   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
394   
395   dbus_address_entries_free (entries);
396
397   /* Different possible errors */
398   if (dbus_parse_address ("foo", &entries, &len, &result))
399     _dbus_assert_not_reached ("Parsed incorrect address.");
400
401   if (dbus_parse_address ("foo:bar", &entries, &len, &result))
402     _dbus_assert_not_reached ("Parsed incorrect address.");
403
404   if (dbus_parse_address ("foo:bar,baz", &entries, &len, &result))
405     _dbus_assert_not_reached ("Parsed incorrect address.");
406
407   if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &result))
408     _dbus_assert_not_reached ("Parsed incorrect address.");
409
410   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &result))
411     _dbus_assert_not_reached ("Parsed incorrect address.");
412
413   if (dbus_parse_address ("foo:=foo", &entries, &len, &result))
414     _dbus_assert_not_reached ("Parsed incorrect address.");
415
416   if (dbus_parse_address ("foo:foo=", &entries, &len, &result))
417     _dbus_assert_not_reached ("Parsed incorrect address.");
418   
419   return TRUE;
420 }
421
422 #endif