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