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
_dbus_set_bad_address(DBusError * error,const char * address_problem_type,const char * address_problem_field,const char * address_problem_other)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
_dbus_address_append_escaped(DBusString * escaped,const DBusString * unescaped)104 _dbus_address_append_escaped (DBusString       *escaped,
105                               const DBusString *unescaped)
106 {
107   const char *p;
108   const 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 = _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
dbus_address_entry_free(DBusAddressEntry * entry)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
dbus_address_entries_free(DBusAddressEntry ** entries)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 *
create_entry(void)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 *
dbus_address_entry_get_method(DBusAddressEntry * entry)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 *
dbus_address_entry_get_value(DBusAddressEntry * entry,const char * key)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
append_unescaped_value(DBusString * unescaped,const DBusString * escaped,int escaped_start,int escaped_len,DBusError * error)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
dbus_parse_address(const char * address,DBusAddressEntry *** entry,int * array_len,DBusError * error)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*
dbus_address_escape_value(const char * value)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*
dbus_address_unescape_value(const char * value,DBusError * error)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 };
683 
684 static const char* invalid_escaped_values[] = {
685   "%a",
686   "%q",
687   "%az",
688   "%%",
689   "%$$",
690   "abc%a",
691   "%axyz",
692   "%",
693   "$",
694   " ",
695 };
696 
697 dbus_bool_t
_dbus_address_test(void)698 _dbus_address_test (void)
699 {
700   DBusAddressEntry **entries;
701   int len;
702   DBusError error = DBUS_ERROR_INIT;
703   int i;
704 
705   i = 0;
706   while (i < _DBUS_N_ELEMENTS (escape_tests))
707     {
708       const EscapeTest *test = &escape_tests[i];
709       char *escaped;
710       char *unescaped;
711 
712       escaped = dbus_address_escape_value (test->unescaped);
713       if (escaped == NULL)
714         _dbus_assert_not_reached ("oom");
715 
716       if (strcmp (escaped, test->escaped) != 0)
717         {
718           _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
719                       test->unescaped, escaped, test->escaped);
720           exit (1);
721         }
722       dbus_free (escaped);
723 
724       unescaped = dbus_address_unescape_value (test->escaped, &error);
725       if (unescaped == NULL)
726         {
727           _dbus_warn ("Failed to unescape '%s': %s\n",
728                       test->escaped, error.message);
729           dbus_error_free (&error);
730           exit (1);
731         }
732 
733       if (strcmp (unescaped, test->unescaped) != 0)
734         {
735           _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
736                       test->escaped, unescaped, test->unescaped);
737           exit (1);
738         }
739       dbus_free (unescaped);
740 
741       ++i;
742     }
743 
744   i = 0;
745   while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
746     {
747       char *unescaped;
748 
749       unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
750                                                &error);
751       if (unescaped != NULL)
752         {
753           _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
754                       invalid_escaped_values[i], unescaped);
755           dbus_free (unescaped);
756           exit (1);
757         }
758 
759       _dbus_assert (dbus_error_is_set (&error));
760       dbus_error_free (&error);
761 
762       ++i;
763     }
764 
765   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
766 			   &entries, &len, &error))
767     _dbus_assert_not_reached ("could not parse address");
768   _dbus_assert (len == 2);
769   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
770   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
771   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
772 
773   dbus_address_entries_free (entries);
774 
775   /* Different possible errors */
776   if (dbus_parse_address ("", &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", &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:bar", &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:bar,baz", &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:bar=foo,baz", &entries, &len, &error))
797     _dbus_assert_not_reached ("Parsed incorrect address.");
798   else
799     dbus_error_free (&error);
800 
801   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
802     _dbus_assert_not_reached ("Parsed incorrect address.");
803   else
804     dbus_error_free (&error);
805 
806   if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
807     _dbus_assert_not_reached ("Parsed incorrect address.");
808   else
809     dbus_error_free (&error);
810 
811   if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
812     _dbus_assert_not_reached ("Parsed incorrect address.");
813   else
814     dbus_error_free (&error);
815 
816   if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
817     _dbus_assert_not_reached ("Parsed incorrect address.");
818   else
819     dbus_error_free (&error);
820 
821   return TRUE;
822 }
823 
824 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
825 
826 #endif
827