* fix mismatching paren typo
[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  * Copyright (C) 2004,2005  Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 2.1
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include <config.h>
26 #include "dbus-address.h"
27 #include "dbus-internals.h"
28 #include "dbus-list.h"
29 #include "dbus-string.h"
30 #include "dbus-protocol.h"
31
32 /**
33  * @defgroup DBusAddressInternals Address parsing
34  * @ingroup  DBusInternals
35  * @brief Implementation of parsing addresses of D-Bus servers.
36  *
37  * @{
38  */
39
40 /**
41  * Internals of DBusAddressEntry 
42  */
43 struct DBusAddressEntry
44 {
45   DBusString method; /**< The address type (unix, tcp, etc.) */
46
47   DBusList *keys;    /**< List of keys */
48   DBusList *values;  /**< List of values */
49 };
50
51 /** @} */ /* End of internals */
52
53 static void
54 dbus_address_entry_free (DBusAddressEntry *entry)
55 {
56   DBusList *link;
57   
58   _dbus_string_free (&entry->method);
59
60   link = _dbus_list_get_first_link (&entry->keys);
61   while (link != NULL)
62     {
63       _dbus_string_free (link->data);
64       dbus_free (link->data);
65       
66       link = _dbus_list_get_next_link (&entry->keys, link);
67     }
68   _dbus_list_clear (&entry->keys);
69   
70   link = _dbus_list_get_first_link (&entry->values);
71   while (link != NULL)
72     {
73       _dbus_string_free (link->data);
74       dbus_free (link->data);
75       
76       link = _dbus_list_get_next_link (&entry->values, link);
77     }
78   _dbus_list_clear (&entry->values);
79   
80   dbus_free (entry);
81 }
82
83 /**
84  * @defgroup DBusAddress Address parsing
85  * @ingroup  DBus
86  * @brief Parsing addresses of D-Bus servers.
87  *
88  * @{
89  */
90
91 /**
92  * Frees a #NULL-terminated array of address entries.
93  *
94  * @param entries the array.
95  */
96 void
97 dbus_address_entries_free (DBusAddressEntry **entries)
98 {
99   int i;
100   
101   for (i = 0; entries[i] != NULL; i++)
102     dbus_address_entry_free (entries[i]);
103   dbus_free (entries);
104 }
105
106 static DBusAddressEntry *
107 create_entry (void)
108 {
109   DBusAddressEntry *entry;
110
111   entry = dbus_new0 (DBusAddressEntry, 1);
112
113   if (entry == NULL)
114     return NULL;
115
116   if (!_dbus_string_init (&entry->method))
117     {
118       dbus_free (entry);
119       return NULL;
120     }
121
122   return entry;
123 }
124
125 /**
126  * Returns the method string of an address entry.
127  *
128  * @param entry the entry.
129  * @returns a string describing the method. This string
130  * must not be freed.
131  */
132 const char *
133 dbus_address_entry_get_method (DBusAddressEntry *entry)
134 {
135   return _dbus_string_get_const_data (&entry->method);
136 }
137
138 /**
139  * Returns a value from a key of an entry.
140  *
141  * @param entry the entry.
142  * @param key the key.
143  * @returns the key value. This string must not be freed.
144  */
145 const char *
146 dbus_address_entry_get_value (DBusAddressEntry *entry,
147                               const char       *key)
148 {
149   DBusList *values, *keys;
150
151   keys = _dbus_list_get_first_link (&entry->keys);
152   values = _dbus_list_get_first_link (&entry->values);
153
154   while (keys != NULL)
155     {
156       _dbus_assert (values != NULL);
157
158       if (_dbus_string_equal_c_str (keys->data, key))
159         return _dbus_string_get_const_data (values->data);
160
161       keys = _dbus_list_get_next_link (&entry->keys, keys);
162       values = _dbus_list_get_next_link (&entry->values, values);
163     }
164   
165   return NULL;
166 }
167
168 #define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b)        \
169          (((b) >= 'a' && (b) <= 'z') ||                 \
170           ((b) >= 'A' && (b) <= 'Z') ||                 \
171           ((b) >= '0' && (b) <= '9') ||                 \
172           (b) == '-' ||                                 \
173           (b) == '_' ||                                 \
174           (b) == '/' ||                                 \
175           (b) == '\\' ||                                \
176           (b) == '.')
177
178 /**
179  * Appends an escaped version of one string to another string,
180  * using the D-Bus address escaping mechanism
181  *
182  * @param escaped the string to append to
183  * @param unescaped the string to escape
184  * @returns #FALSE if no memory
185  */
186 dbus_bool_t
187 _dbus_address_append_escaped (DBusString       *escaped,
188                               const DBusString *unescaped)
189 {
190   const char *p;
191   const char *end;
192   dbus_bool_t ret;
193   int orig_len;
194
195   ret = FALSE;
196
197   orig_len = _dbus_string_get_length (escaped);
198   p = _dbus_string_get_const_data (unescaped);
199   end = p + _dbus_string_get_length (unescaped);
200   while (p != end)
201     {
202       if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
203         {
204           if (!_dbus_string_append_byte (escaped, *p))
205             goto out;
206         }
207       else
208         {
209           if (!_dbus_string_append_byte (escaped, '%'))
210             goto out;
211           if (!_dbus_string_append_byte_as_hex (escaped, *p))
212             goto out;
213         }
214       
215       ++p;
216     }
217
218   ret = TRUE;
219   
220  out:
221   if (!ret)
222     _dbus_string_set_length (escaped, orig_len);
223   return ret;
224 }
225
226 static dbus_bool_t
227 append_unescaped_value (DBusString       *unescaped,
228                         const DBusString *escaped,
229                         int               escaped_start,
230                         int               escaped_len,
231                         DBusError        *error)
232 {
233   const char *p;
234   const char *end;
235   dbus_bool_t ret;
236   
237   ret = FALSE;
238
239   p = _dbus_string_get_const_data (escaped) + escaped_start;
240   end = p + escaped_len;
241   while (p != end)
242     {
243       if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
244         {
245           if (!_dbus_string_append_byte (unescaped, *p))
246             goto out;
247         }
248       else if (*p == '%')
249         {
250           /* Efficiency is king */
251           char buf[3];
252           DBusString hex;
253           int hex_end;
254           
255           ++p;
256
257           if ((p + 2) > end)
258             {
259               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
260                               "In D-Bus address, percent character was not followed by two hex digits");
261               goto out;
262             }
263             
264           buf[0] = *p;
265           ++p;
266           buf[1] = *p;
267           buf[2] = '\0';
268
269           _dbus_string_init_const (&hex, buf);
270
271           if (!_dbus_string_hex_decode (&hex, 0, &hex_end,
272                                         unescaped,
273                                         _dbus_string_get_length (unescaped)))
274             goto out;
275
276           if (hex_end != 2)
277             {
278               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
279                               "In D-Bus address, percent character was followed by characters other than hex digits");
280               goto out;
281             }
282         }
283       else
284         {
285           /* Error, should have been escaped */
286           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
287                           "In D-Bus address, character '%c' should have been escaped\n",
288                           *p);
289           goto out;
290         }
291       
292       ++p;
293     }
294
295   ret = TRUE;
296   
297  out:
298   if (!ret && error && !dbus_error_is_set (error))
299     _DBUS_SET_OOM (error);
300
301   _dbus_assert (ret || error == NULL || dbus_error_is_set (error));
302   
303   return ret;
304 }
305
306 /**
307  * Parses an address string of the form:
308  *
309  * method:key=value,key=value;method:key=value
310  *
311  * See the D-Bus specification for complete docs on the format.
312  * 
313  * @param address the address.
314  * @param entry return location to an array of entries.
315  * @param array_len return location for array length.
316  * @param error address where an error can be returned.
317  * @returns #TRUE on success, #FALSE otherwise.
318  */
319 dbus_bool_t
320 dbus_parse_address (const char         *address,
321                     DBusAddressEntry ***entry,
322                     int                *array_len,
323                     DBusError          *error)
324 {
325   DBusString str;
326   int pos, end_pos, len, i;
327   DBusList *entries, *link;
328   DBusAddressEntry **entry_array;
329
330   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
331   
332   _dbus_string_init_const (&str, address);
333
334   entries = NULL;
335   pos = 0;
336   len = _dbus_string_get_length (&str);
337   
338   while (pos < len)
339     {
340       DBusAddressEntry *entry;
341
342       int found_pos;
343
344       entry = create_entry ();
345       if (!entry)
346         {
347           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
348
349           goto error;
350         }
351       
352       /* Append the entry */
353       if (!_dbus_list_append (&entries, entry))
354         {
355           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
356           dbus_address_entry_free (entry);
357           goto error;
358         }
359       
360       /* Look for a semi-colon */
361       if (!_dbus_string_find (&str, pos, ";", &end_pos))
362         end_pos = len;
363       
364       /* Look for the colon : */
365       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
366         {
367           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
368           goto error;
369         }
370
371       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
372         {
373           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
374           goto error;
375         }
376           
377       pos = found_pos + 1;
378
379       while (pos < end_pos)
380         {
381           int comma_pos, equals_pos;
382
383           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
384             comma_pos = end_pos;
385           
386           if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
387               equals_pos == pos || equals_pos + 1 == comma_pos)
388             {
389               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
390                               "'=' character not found or has no value following it");
391               goto error;
392             }
393           else
394             {
395               DBusString *key;
396               DBusString *value;
397
398               key = dbus_new0 (DBusString, 1);
399
400               if (!key)
401                 {
402                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
403                   goto error;
404                 }
405
406               value = dbus_new0 (DBusString, 1);
407               if (!value)
408                 {
409                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
410                   dbus_free (key);
411                   goto error;
412                 }
413               
414               if (!_dbus_string_init (key))
415                 {
416                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
417                   dbus_free (key);
418                   dbus_free (value);
419                   
420                   goto error;
421                 }
422               
423               if (!_dbus_string_init (value))
424                 {
425                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
426                   _dbus_string_free (key);
427
428                   dbus_free (key);
429                   dbus_free (value);              
430                   goto error;
431                 }
432
433               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
434                 {
435                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
436                   _dbus_string_free (key);
437                   _dbus_string_free (value);
438
439                   dbus_free (key);
440                   dbus_free (value);              
441                   goto error;
442                 }
443
444               if (!append_unescaped_value (value, &str, equals_pos + 1,
445                                            comma_pos - equals_pos - 1, error))
446                 {
447                   _dbus_assert (error == NULL || dbus_error_is_set (error));
448                   _dbus_string_free (key);
449                   _dbus_string_free (value);
450
451                   dbus_free (key);
452                   dbus_free (value);              
453                   goto error;
454                 }
455
456               if (!_dbus_list_append (&entry->keys, key))
457                 {
458                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
459                   _dbus_string_free (key);
460                   _dbus_string_free (value);
461
462                   dbus_free (key);
463                   dbus_free (value);              
464                   goto error;
465                 }
466
467               if (!_dbus_list_append (&entry->values, value))
468                 {
469                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
470                   _dbus_string_free (value);
471
472                   dbus_free (value);
473                   goto error;             
474                 }
475             }
476
477           pos = comma_pos + 1;
478         }
479
480       pos = end_pos + 1;
481     }
482
483   *array_len = _dbus_list_get_length (&entries);
484   
485   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
486
487   if (!entry_array)
488     {
489       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
490       
491       goto error;
492     }
493   
494   entry_array [*array_len] = NULL;
495
496   link = _dbus_list_get_first_link (&entries);
497   i = 0;
498   while (link != NULL)
499     {
500       entry_array[i] = link->data;
501       i++;
502       link = _dbus_list_get_next_link (&entries, link);
503     }
504
505   _dbus_list_clear (&entries);
506   *entry = entry_array;
507
508   return TRUE;
509
510  error:
511   
512   link = _dbus_list_get_first_link (&entries);
513   while (link != NULL)
514     {
515       dbus_address_entry_free (link->data);
516       link = _dbus_list_get_next_link (&entries, link);
517     }
518
519   _dbus_list_clear (&entries);
520   
521   return FALSE;
522   
523 }
524
525 /**
526  * Escapes the given string as a value in a key=value pair
527  * for a D-Bus address.
528  *
529  * @param value the unescaped value
530  * @returns newly-allocated escaped value or #NULL if no memory
531  */
532 char*
533 dbus_address_escape_value (const char *value)
534 {
535   DBusString escaped;
536   DBusString unescaped;
537   char *ret;
538
539   ret = NULL;
540
541   _dbus_string_init_const (&unescaped, value);
542   
543   if (!_dbus_string_init (&escaped))
544     return NULL;
545
546   if (!_dbus_address_append_escaped (&escaped, &unescaped))
547     goto out;
548   
549   if (!_dbus_string_steal_data (&escaped, &ret))
550     goto out;
551
552  out:
553   _dbus_string_free (&escaped);
554   return ret;
555 }
556
557 /**
558  * Unescapes the given string as a value in a key=value pair
559  * for a D-Bus address.
560  *
561  * @param value the escaped value
562  * @param error error to set if the unescaping fails
563  * @returns newly-allocated unescaped value or #NULL if no memory
564  */
565 char*
566 dbus_address_unescape_value (const char *value,
567                              DBusError  *error)
568 {
569   DBusString unescaped;
570   DBusString escaped;
571   char *ret;
572   
573   ret = NULL;
574
575   _dbus_string_init_const (&escaped, value);
576   
577   if (!_dbus_string_init (&unescaped))
578     return NULL;
579
580   if (!append_unescaped_value (&unescaped, &escaped,
581                                0, _dbus_string_get_length (&escaped),
582                                error))
583     goto out;
584   
585   if (!_dbus_string_steal_data (&unescaped, &ret))
586     goto out;
587
588  out:
589   if (ret == NULL && error && !dbus_error_is_set (error))
590     _DBUS_SET_OOM (error);
591
592   _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
593   
594   _dbus_string_free (&unescaped);
595   return ret;
596 }
597
598 /** @} */ /* End of public API */
599
600 #ifdef DBUS_BUILD_TESTS
601 #include "dbus-test.h"
602 #include <stdlib.h>
603
604 typedef struct
605 {
606   const char *escaped;
607   const char *unescaped;
608 } EscapeTest;
609
610 static const EscapeTest escape_tests[] = {
611   { "abcde", "abcde" },
612   { "", "" },
613   { "%20%20", "  " },
614   { "%24", "$" },
615   { "%25", "%" },
616   { "abc%24", "abc$" },
617   { "%24abc", "$abc" },
618   { "abc%24abc", "abc$abc" },
619   { "/", "/" },
620   { "-", "-" },
621   { "_", "_" },
622   { "A", "A" },
623   { "I", "I" },
624   { "Z", "Z" },
625   { "a", "a" },
626   { "i", "i" },
627   { "z", "z" }
628 };
629
630 static const char* invalid_escaped_values[] = {
631   "%a",
632   "%q",
633   "%az",
634   "%%",
635   "%$$",
636   "abc%a",
637   "%axyz",
638   "%",
639   "$",
640   " ",
641   "*"
642 };
643
644 dbus_bool_t
645 _dbus_address_test (void)
646 {
647   DBusAddressEntry **entries;
648   int len;  
649   DBusError error;
650   int i;
651
652   dbus_error_init (&error);
653
654   i = 0;
655   while (i < _DBUS_N_ELEMENTS (escape_tests))
656     {
657       const EscapeTest *test = &escape_tests[i];
658       char *escaped;
659       char *unescaped;
660
661       escaped = dbus_address_escape_value (test->unescaped);
662       if (escaped == NULL)
663         _dbus_assert_not_reached ("oom");
664
665       if (strcmp (escaped, test->escaped) != 0)
666         {
667           _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
668                       test->unescaped, escaped, test->escaped);
669           exit (1);
670         }
671       dbus_free (escaped);
672
673       unescaped = dbus_address_unescape_value (test->escaped, &error);
674       if (unescaped == NULL)
675         {
676           _dbus_warn ("Failed to unescape '%s': %s\n",
677                       test->escaped, error.message);
678           dbus_error_free (&error);
679           exit (1);
680         }
681
682       if (strcmp (unescaped, test->unescaped) != 0)
683         {
684           _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
685                       test->escaped, unescaped, test->unescaped);
686           exit (1);
687         }
688       dbus_free (unescaped);
689       
690       ++i;
691     }
692
693   i = 0;
694   while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
695     {
696       char *unescaped;
697
698       unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
699                                                &error);
700       if (unescaped != NULL)
701         {
702           _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
703                       invalid_escaped_values[i], unescaped);
704           dbus_free (unescaped);
705           exit (1);
706         }
707
708       _dbus_assert (dbus_error_is_set (&error));
709       dbus_error_free (&error);
710
711       ++i;
712     }
713   
714   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
715                            &entries, &len, &error))
716     _dbus_assert_not_reached ("could not parse address");
717   _dbus_assert (len == 2);
718   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
719   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
720   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
721   
722   dbus_address_entries_free (entries);
723
724   /* Different possible errors */
725   if (dbus_parse_address ("foo", &entries, &len, &error))
726     _dbus_assert_not_reached ("Parsed incorrect address.");
727   else
728     dbus_error_free (&error);
729   
730   if (dbus_parse_address ("foo:bar", &entries, &len, &error))
731     _dbus_assert_not_reached ("Parsed incorrect address.");
732   else
733     dbus_error_free (&error);
734   
735   if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
736     _dbus_assert_not_reached ("Parsed incorrect address.");
737   else
738     dbus_error_free (&error);
739   
740   if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
741     _dbus_assert_not_reached ("Parsed incorrect address.");
742   else
743     dbus_error_free (&error);
744   
745   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
746     _dbus_assert_not_reached ("Parsed incorrect address.");
747   else
748     dbus_error_free (&error);
749   
750   if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
751     _dbus_assert_not_reached ("Parsed incorrect address.");
752   else
753     dbus_error_free (&error);
754   
755   if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
756     _dbus_assert_not_reached ("Parsed incorrect address.");
757   else
758     dbus_error_free (&error);
759
760   if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
761     _dbus_assert_not_reached ("Parsed incorrect address.");
762   else
763     dbus_error_free (&error);
764
765   return TRUE;
766 }
767
768 #endif