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