D-Bus 1.4.1
|
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 00002 /* dbus-address.c Server address parser. 00003 * 00004 * Copyright (C) 2003 CodeFactory AB 00005 * Copyright (C) 2004,2005 Red Hat, Inc. 00006 * 00007 * Licensed under the Academic Free License version 2.1 00008 * 00009 * This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00022 * 00023 */ 00024 00025 #include <config.h> 00026 #include "dbus-address.h" 00027 #include "dbus-internals.h" 00028 #include "dbus-list.h" 00029 #include "dbus-string.h" 00030 #include "dbus-protocol.h" 00031 00043 struct DBusAddressEntry 00044 { 00045 DBusString method; 00047 DBusList *keys; 00048 DBusList *values; 00049 }; 00050 00051 00064 void 00065 _dbus_set_bad_address (DBusError *error, 00066 const char *address_problem_type, 00067 const char *address_problem_field, 00068 const char *address_problem_other) 00069 { 00070 if (address_problem_type != NULL) 00071 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00072 "Server address of type %s was missing argument %s", 00073 address_problem_type, address_problem_field); 00074 else 00075 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00076 "Could not parse server address: %s", 00077 address_problem_other); 00078 } 00079 00084 #define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \ 00085 (((b) >= 'a' && (b) <= 'z') || \ 00086 ((b) >= 'A' && (b) <= 'Z') || \ 00087 ((b) >= '0' && (b) <= '9') || \ 00088 (b) == '-' || \ 00089 (b) == '_' || \ 00090 (b) == '/' || \ 00091 (b) == '\\' || \ 00092 (b) == '*' || \ 00093 (b) == '.') 00094 00103 dbus_bool_t 00104 _dbus_address_append_escaped (DBusString *escaped, 00105 const DBusString *unescaped) 00106 { 00107 const char *p; 00108 const char *end; 00109 dbus_bool_t ret; 00110 int orig_len; 00111 00112 ret = FALSE; 00113 00114 orig_len = _dbus_string_get_length (escaped); 00115 p = _dbus_string_get_const_data (unescaped); 00116 end = p + _dbus_string_get_length (unescaped); 00117 while (p != end) 00118 { 00119 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) 00120 { 00121 if (!_dbus_string_append_byte (escaped, *p)) 00122 goto out; 00123 } 00124 else 00125 { 00126 if (!_dbus_string_append_byte (escaped, '%')) 00127 goto out; 00128 if (!_dbus_string_append_byte_as_hex (escaped, *p)) 00129 goto out; 00130 } 00131 00132 ++p; 00133 } 00134 00135 ret = TRUE; 00136 00137 out: 00138 if (!ret) 00139 _dbus_string_set_length (escaped, orig_len); 00140 return ret; 00141 } 00142 /* End of internals */ 00144 00145 static void 00146 dbus_address_entry_free (DBusAddressEntry *entry) 00147 { 00148 DBusList *link; 00149 00150 _dbus_string_free (&entry->method); 00151 00152 link = _dbus_list_get_first_link (&entry->keys); 00153 while (link != NULL) 00154 { 00155 _dbus_string_free (link->data); 00156 dbus_free (link->data); 00157 00158 link = _dbus_list_get_next_link (&entry->keys, link); 00159 } 00160 _dbus_list_clear (&entry->keys); 00161 00162 link = _dbus_list_get_first_link (&entry->values); 00163 while (link != NULL) 00164 { 00165 _dbus_string_free (link->data); 00166 dbus_free (link->data); 00167 00168 link = _dbus_list_get_next_link (&entry->values, link); 00169 } 00170 _dbus_list_clear (&entry->values); 00171 00172 dbus_free (entry); 00173 } 00174 00188 void 00189 dbus_address_entries_free (DBusAddressEntry **entries) 00190 { 00191 int i; 00192 00193 for (i = 0; entries[i] != NULL; i++) 00194 dbus_address_entry_free (entries[i]); 00195 dbus_free (entries); 00196 } 00197 00198 static DBusAddressEntry * 00199 create_entry (void) 00200 { 00201 DBusAddressEntry *entry; 00202 00203 entry = dbus_new0 (DBusAddressEntry, 1); 00204 00205 if (entry == NULL) 00206 return NULL; 00207 00208 if (!_dbus_string_init (&entry->method)) 00209 { 00210 dbus_free (entry); 00211 return NULL; 00212 } 00213 00214 return entry; 00215 } 00216 00226 const char * 00227 dbus_address_entry_get_method (DBusAddressEntry *entry) 00228 { 00229 return _dbus_string_get_const_data (&entry->method); 00230 } 00231 00243 const char * 00244 dbus_address_entry_get_value (DBusAddressEntry *entry, 00245 const char *key) 00246 { 00247 DBusList *values, *keys; 00248 00249 keys = _dbus_list_get_first_link (&entry->keys); 00250 values = _dbus_list_get_first_link (&entry->values); 00251 00252 while (keys != NULL) 00253 { 00254 _dbus_assert (values != NULL); 00255 00256 if (_dbus_string_equal_c_str (keys->data, key)) 00257 return _dbus_string_get_const_data (values->data); 00258 00259 keys = _dbus_list_get_next_link (&entry->keys, keys); 00260 values = _dbus_list_get_next_link (&entry->values, values); 00261 } 00262 00263 return NULL; 00264 } 00265 00266 static dbus_bool_t 00267 append_unescaped_value (DBusString *unescaped, 00268 const DBusString *escaped, 00269 int escaped_start, 00270 int escaped_len, 00271 DBusError *error) 00272 { 00273 const char *p; 00274 const char *end; 00275 dbus_bool_t ret; 00276 00277 ret = FALSE; 00278 00279 p = _dbus_string_get_const_data (escaped) + escaped_start; 00280 end = p + escaped_len; 00281 while (p != end) 00282 { 00283 if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) 00284 { 00285 if (!_dbus_string_append_byte (unescaped, *p)) 00286 goto out; 00287 } 00288 else if (*p == '%') 00289 { 00290 /* Efficiency is king */ 00291 char buf[3]; 00292 DBusString hex; 00293 int hex_end; 00294 00295 ++p; 00296 00297 if ((p + 2) > end) 00298 { 00299 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00300 "In D-Bus address, percent character was not followed by two hex digits"); 00301 goto out; 00302 } 00303 00304 buf[0] = *p; 00305 ++p; 00306 buf[1] = *p; 00307 buf[2] = '\0'; 00308 00309 _dbus_string_init_const (&hex, buf); 00310 00311 if (!_dbus_string_hex_decode (&hex, 0, &hex_end, 00312 unescaped, 00313 _dbus_string_get_length (unescaped))) 00314 goto out; 00315 00316 if (hex_end != 2) 00317 { 00318 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00319 "In D-Bus address, percent character was followed by characters other than hex digits"); 00320 goto out; 00321 } 00322 } 00323 else 00324 { 00325 /* Error, should have been escaped */ 00326 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00327 "In D-Bus address, character '%c' should have been escaped\n", 00328 *p); 00329 goto out; 00330 } 00331 00332 ++p; 00333 } 00334 00335 ret = TRUE; 00336 00337 out: 00338 if (!ret && error && !dbus_error_is_set (error)) 00339 _DBUS_SET_OOM (error); 00340 00341 _dbus_assert (ret || error == NULL || dbus_error_is_set (error)); 00342 00343 return ret; 00344 } 00345 00362 dbus_bool_t 00363 dbus_parse_address (const char *address, 00364 DBusAddressEntry ***entry, 00365 int *array_len, 00366 DBusError *error) 00367 { 00368 DBusString str; 00369 int pos, end_pos, len, i; 00370 DBusList *entries, *link; 00371 DBusAddressEntry **entry_array; 00372 00373 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 00374 00375 _dbus_string_init_const (&str, address); 00376 00377 entries = NULL; 00378 pos = 0; 00379 len = _dbus_string_get_length (&str); 00380 00381 if (len == 0) 00382 { 00383 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00384 "Empty address '%s'", address); 00385 goto error; 00386 } 00387 00388 while (pos < len) 00389 { 00390 DBusAddressEntry *entry; 00391 00392 int found_pos; 00393 00394 entry = create_entry (); 00395 if (!entry) 00396 { 00397 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00398 00399 goto error; 00400 } 00401 00402 /* Append the entry */ 00403 if (!_dbus_list_append (&entries, entry)) 00404 { 00405 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00406 dbus_address_entry_free (entry); 00407 goto error; 00408 } 00409 00410 /* Look for a semi-colon */ 00411 if (!_dbus_string_find (&str, pos, ";", &end_pos)) 00412 end_pos = len; 00413 00414 /* Look for the colon : */ 00415 if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos)) 00416 { 00417 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon"); 00418 goto error; 00419 } 00420 00421 if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0)) 00422 { 00423 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00424 goto error; 00425 } 00426 00427 pos = found_pos + 1; 00428 00429 while (pos < end_pos) 00430 { 00431 int comma_pos, equals_pos; 00432 00433 if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos)) 00434 comma_pos = end_pos; 00435 00436 if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) || 00437 equals_pos == pos || equals_pos + 1 == comma_pos) 00438 { 00439 dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, 00440 "'=' character not found or has no value following it"); 00441 goto error; 00442 } 00443 else 00444 { 00445 DBusString *key; 00446 DBusString *value; 00447 00448 key = dbus_new0 (DBusString, 1); 00449 00450 if (!key) 00451 { 00452 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00453 goto error; 00454 } 00455 00456 value = dbus_new0 (DBusString, 1); 00457 if (!value) 00458 { 00459 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00460 dbus_free (key); 00461 goto error; 00462 } 00463 00464 if (!_dbus_string_init (key)) 00465 { 00466 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00467 dbus_free (key); 00468 dbus_free (value); 00469 00470 goto error; 00471 } 00472 00473 if (!_dbus_string_init (value)) 00474 { 00475 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00476 _dbus_string_free (key); 00477 00478 dbus_free (key); 00479 dbus_free (value); 00480 goto error; 00481 } 00482 00483 if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0)) 00484 { 00485 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00486 _dbus_string_free (key); 00487 _dbus_string_free (value); 00488 00489 dbus_free (key); 00490 dbus_free (value); 00491 goto error; 00492 } 00493 00494 if (!append_unescaped_value (value, &str, equals_pos + 1, 00495 comma_pos - equals_pos - 1, error)) 00496 { 00497 _dbus_assert (error == NULL || dbus_error_is_set (error)); 00498 _dbus_string_free (key); 00499 _dbus_string_free (value); 00500 00501 dbus_free (key); 00502 dbus_free (value); 00503 goto error; 00504 } 00505 00506 if (!_dbus_list_append (&entry->keys, key)) 00507 { 00508 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00509 _dbus_string_free (key); 00510 _dbus_string_free (value); 00511 00512 dbus_free (key); 00513 dbus_free (value); 00514 goto error; 00515 } 00516 00517 if (!_dbus_list_append (&entry->values, value)) 00518 { 00519 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00520 _dbus_string_free (value); 00521 00522 dbus_free (value); 00523 goto error; 00524 } 00525 } 00526 00527 pos = comma_pos + 1; 00528 } 00529 00530 pos = end_pos + 1; 00531 } 00532 00533 *array_len = _dbus_list_get_length (&entries); 00534 00535 entry_array = dbus_new (DBusAddressEntry *, *array_len + 1); 00536 00537 if (!entry_array) 00538 { 00539 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 00540 00541 goto error; 00542 } 00543 00544 entry_array [*array_len] = NULL; 00545 00546 link = _dbus_list_get_first_link (&entries); 00547 i = 0; 00548 while (link != NULL) 00549 { 00550 entry_array[i] = link->data; 00551 i++; 00552 link = _dbus_list_get_next_link (&entries, link); 00553 } 00554 00555 _dbus_list_clear (&entries); 00556 *entry = entry_array; 00557 00558 return TRUE; 00559 00560 error: 00561 00562 link = _dbus_list_get_first_link (&entries); 00563 while (link != NULL) 00564 { 00565 dbus_address_entry_free (link->data); 00566 link = _dbus_list_get_next_link (&entries, link); 00567 } 00568 00569 _dbus_list_clear (&entries); 00570 00571 return FALSE; 00572 00573 } 00574 00582 char* 00583 dbus_address_escape_value (const char *value) 00584 { 00585 DBusString escaped; 00586 DBusString unescaped; 00587 char *ret; 00588 00589 ret = NULL; 00590 00591 _dbus_string_init_const (&unescaped, value); 00592 00593 if (!_dbus_string_init (&escaped)) 00594 return NULL; 00595 00596 if (!_dbus_address_append_escaped (&escaped, &unescaped)) 00597 goto out; 00598 00599 if (!_dbus_string_steal_data (&escaped, &ret)) 00600 goto out; 00601 00602 out: 00603 _dbus_string_free (&escaped); 00604 return ret; 00605 } 00606 00616 char* 00617 dbus_address_unescape_value (const char *value, 00618 DBusError *error) 00619 { 00620 DBusString unescaped; 00621 DBusString escaped; 00622 char *ret; 00623 00624 ret = NULL; 00625 00626 _dbus_string_init_const (&escaped, value); 00627 00628 if (!_dbus_string_init (&unescaped)) 00629 return NULL; 00630 00631 if (!append_unescaped_value (&unescaped, &escaped, 00632 0, _dbus_string_get_length (&escaped), 00633 error)) 00634 goto out; 00635 00636 if (!_dbus_string_steal_data (&unescaped, &ret)) 00637 goto out; 00638 00639 out: 00640 if (ret == NULL && error && !dbus_error_is_set (error)) 00641 _DBUS_SET_OOM (error); 00642 00643 _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error)); 00644 00645 _dbus_string_free (&unescaped); 00646 return ret; 00647 } 00648 /* End of public API */ 00650 00651 #ifdef DBUS_BUILD_TESTS 00652 00653 #ifndef DOXYGEN_SHOULD_SKIP_THIS 00654 00655 #include "dbus-test.h" 00656 #include <stdlib.h> 00657 00658 typedef struct 00659 { 00660 const char *escaped; 00661 const char *unescaped; 00662 } EscapeTest; 00663 00664 static const EscapeTest escape_tests[] = { 00665 { "abcde", "abcde" }, 00666 { "", "" }, 00667 { "%20%20", " " }, 00668 { "%24", "$" }, 00669 { "%25", "%" }, 00670 { "abc%24", "abc$" }, 00671 { "%24abc", "$abc" }, 00672 { "abc%24abc", "abc$abc" }, 00673 { "/", "/" }, 00674 { "-", "-" }, 00675 { "_", "_" }, 00676 { "A", "A" }, 00677 { "I", "I" }, 00678 { "Z", "Z" }, 00679 { "a", "a" }, 00680 { "i", "i" }, 00681 { "z", "z" } 00682 }; 00683 00684 static const char* invalid_escaped_values[] = { 00685 "%a", 00686 "%q", 00687 "%az", 00688 "%%", 00689 "%$$", 00690 "abc%a", 00691 "%axyz", 00692 "%", 00693 "$", 00694 " ", 00695 }; 00696 00697 dbus_bool_t 00698 _dbus_address_test (void) 00699 { 00700 DBusAddressEntry **entries; 00701 int len; 00702 DBusError error = DBUS_ERROR_INIT; 00703 int i; 00704 00705 i = 0; 00706 while (i < _DBUS_N_ELEMENTS (escape_tests)) 00707 { 00708 const EscapeTest *test = &escape_tests[i]; 00709 char *escaped; 00710 char *unescaped; 00711 00712 escaped = dbus_address_escape_value (test->unescaped); 00713 if (escaped == NULL) 00714 _dbus_assert_not_reached ("oom"); 00715 00716 if (strcmp (escaped, test->escaped) != 0) 00717 { 00718 _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n", 00719 test->unescaped, escaped, test->escaped); 00720 exit (1); 00721 } 00722 dbus_free (escaped); 00723 00724 unescaped = dbus_address_unescape_value (test->escaped, &error); 00725 if (unescaped == NULL) 00726 { 00727 _dbus_warn ("Failed to unescape '%s': %s\n", 00728 test->escaped, error.message); 00729 dbus_error_free (&error); 00730 exit (1); 00731 } 00732 00733 if (strcmp (unescaped, test->unescaped) != 0) 00734 { 00735 _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n", 00736 test->escaped, unescaped, test->unescaped); 00737 exit (1); 00738 } 00739 dbus_free (unescaped); 00740 00741 ++i; 00742 } 00743 00744 i = 0; 00745 while (i < _DBUS_N_ELEMENTS (invalid_escaped_values)) 00746 { 00747 char *unescaped; 00748 00749 unescaped = dbus_address_unescape_value (invalid_escaped_values[i], 00750 &error); 00751 if (unescaped != NULL) 00752 { 00753 _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n", 00754 invalid_escaped_values[i], unescaped); 00755 dbus_free (unescaped); 00756 exit (1); 00757 } 00758 00759 _dbus_assert (dbus_error_is_set (&error)); 00760 dbus_error_free (&error); 00761 00762 ++i; 00763 } 00764 00765 if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;", 00766 &entries, &len, &error)) 00767 _dbus_assert_not_reached ("could not parse address"); 00768 _dbus_assert (len == 2); 00769 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0); 00770 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0); 00771 _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0); 00772 00773 dbus_address_entries_free (entries); 00774 00775 /* Different possible errors */ 00776 if (dbus_parse_address ("", &entries, &len, &error)) 00777 _dbus_assert_not_reached ("Parsed incorrect address."); 00778 else 00779 dbus_error_free (&error); 00780 00781 if (dbus_parse_address ("foo", &entries, &len, &error)) 00782 _dbus_assert_not_reached ("Parsed incorrect address."); 00783 else 00784 dbus_error_free (&error); 00785 00786 if (dbus_parse_address ("foo:bar", &entries, &len, &error)) 00787 _dbus_assert_not_reached ("Parsed incorrect address."); 00788 else 00789 dbus_error_free (&error); 00790 00791 if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error)) 00792 _dbus_assert_not_reached ("Parsed incorrect address."); 00793 else 00794 dbus_error_free (&error); 00795 00796 if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error)) 00797 _dbus_assert_not_reached ("Parsed incorrect address."); 00798 else 00799 dbus_error_free (&error); 00800 00801 if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error)) 00802 _dbus_assert_not_reached ("Parsed incorrect address."); 00803 else 00804 dbus_error_free (&error); 00805 00806 if (dbus_parse_address ("foo:=foo", &entries, &len, &error)) 00807 _dbus_assert_not_reached ("Parsed incorrect address."); 00808 else 00809 dbus_error_free (&error); 00810 00811 if (dbus_parse_address ("foo:foo=", &entries, &len, &error)) 00812 _dbus_assert_not_reached ("Parsed incorrect address."); 00813 else 00814 dbus_error_free (&error); 00815 00816 if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error)) 00817 _dbus_assert_not_reached ("Parsed incorrect address."); 00818 else 00819 dbus_error_free (&error); 00820 00821 return TRUE; 00822 } 00823 00824 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */ 00825 00826 #endif