X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-address.c;h=c4cfbbe59770f7feac786700849c0923e670fbb8;hb=1200c464b6c9051340960e07f0d61a51dad71286;hp=ef8a51da2a9c8ecb0fe951f2e46029927fd66efa;hpb=23e4978e2f07de14449f63320cd5e6b066672b8e;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-address.c b/dbus/dbus-address.c index ef8a51d..c4cfbbe 100644 --- a/dbus/dbus-address.c +++ b/dbus/dbus-address.c @@ -1,9 +1,10 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-address.c Server address parser. * * Copyright (C) 2003 CodeFactory AB + * Copyright (C) 2004,2005 Red Hat, Inc. * - * Licensed under the Academic Free License version 1.2 + * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ @@ -25,22 +26,122 @@ #include "dbus-address.h" #include "dbus-internals.h" #include "dbus-list.h" +#include "dbus-string.h" +#include "dbus-protocol.h" /** - * @defgroup DBusAddress Address parsing - * @ingroup DBus - * @brief Parsing addresses of D-BUS servers. + * @defgroup DBusAddressInternals Address parsing + * @ingroup DBusInternals + * @brief Implementation of parsing addresses of D-Bus servers. * * @{ */ + +/** + * Internals of DBusAddressEntry + */ struct DBusAddressEntry { - DBusString method; + DBusString method; /**< The address type (unix, tcp, etc.) */ - DBusList *keys; - DBusList *values; + DBusList *keys; /**< List of keys */ + DBusList *values; /**< List of values */ }; + +/** + * + * Sets #DBUS_ERROR_BAD_ADDRESS. + * If address_problem_type and address_problem_field are not #NULL, + * sets an error message about how the field is no good. Otherwise, sets + * address_problem_other as the error message. + * + * @param error the error to set + * @param address_problem_type the address type of the bad address or #NULL + * @param address_problem_field the missing field of the bad address or #NULL + * @param address_problem_other any other error message or #NULL + */ +void +_dbus_set_bad_address (DBusError *error, + const char *address_problem_type, + const char *address_problem_field, + const char *address_problem_other) +{ + if (address_problem_type != NULL) + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Server address of type %s was missing argument %s", + address_problem_type, address_problem_field); + else + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Could not parse server address: %s", + address_problem_other); +} + +/** + * #TRUE if the byte need not be escaped when found in a dbus address. + * All other bytes are required to be escaped in a valid address. + */ +#define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b) \ + (((b) >= 'a' && (b) <= 'z') || \ + ((b) >= 'A' && (b) <= 'Z') || \ + ((b) >= '0' && (b) <= '9') || \ + (b) == '-' || \ + (b) == '_' || \ + (b) == '/' || \ + (b) == '\\' || \ + (b) == '*' || \ + (b) == '.') + +/** + * Appends an escaped version of one string to another string, + * using the D-Bus address escaping mechanism + * + * @param escaped the string to append to + * @param unescaped the string to escape + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_address_append_escaped (DBusString *escaped, + const DBusString *unescaped) +{ + const unsigned char *p; + const unsigned char *end; + dbus_bool_t ret; + int orig_len; + + ret = FALSE; + + orig_len = _dbus_string_get_length (escaped); + p = (const unsigned char *) _dbus_string_get_const_data (unescaped); + end = p + _dbus_string_get_length (unescaped); + while (p != end) + { + if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) + { + if (!_dbus_string_append_byte (escaped, *p)) + goto out; + } + else + { + if (!_dbus_string_append_byte (escaped, '%')) + goto out; + if (!_dbus_string_append_byte_as_hex (escaped, *p)) + goto out; + } + + ++p; + } + + ret = TRUE; + + out: + if (!ret) + _dbus_string_set_length (escaped, orig_len); + return ret; +} + +/** @} */ /* End of internals */ + static void dbus_address_entry_free (DBusAddressEntry *entry) { @@ -71,6 +172,13 @@ dbus_address_entry_free (DBusAddressEntry *entry) dbus_free (entry); } +/** + * @defgroup DBusAddress Address parsing + * @ingroup DBus + * @brief Parsing addresses of D-Bus servers. + * + * @{ + */ /** * Frees a #NULL-terminated array of address entries. @@ -97,7 +205,7 @@ create_entry (void) if (entry == NULL) return NULL; - if (!_dbus_string_init (&entry->method, _DBUS_INT_MAX)) + if (!_dbus_string_init (&entry->method)) { dbus_free (entry); return NULL; @@ -107,7 +215,9 @@ create_entry (void) } /** - * Returns the method string of an address entry. + * Returns the method string of an address entry. For example, given + * the address entry "tcp:host=example.com" it would return the string + * "tcp" * * @param entry the entry. * @returns a string describing the method. This string @@ -116,19 +226,19 @@ create_entry (void) const char * dbus_address_entry_get_method (DBusAddressEntry *entry) { - const char *method; - - _dbus_string_get_const_data (&entry->method, &method); - - return method; + return _dbus_string_get_const_data (&entry->method); } /** - * Returns a value from a key of an entry. + * Returns a value from a key of an entry. For example, + * given the address "tcp:host=example.com,port=8073" if you asked + * for the key "host" you would get the value "example.com" * + * The returned value is already unescaped. + * * @param entry the entry. * @param key the key. - * @returns the key value. This string must not be fred. + * @returns the key value. This string must not be freed. */ const char * dbus_address_entry_get_value (DBusAddressEntry *entry, @@ -144,12 +254,8 @@ dbus_address_entry_get_value (DBusAddressEntry *entry, _dbus_assert (values != NULL); if (_dbus_string_equal_c_str (keys->data, key)) - { - const char *str; + return _dbus_string_get_const_data (values->data); - _dbus_string_get_const_data (values->data, &str); - return str; - } keys = _dbus_list_get_next_link (&entry->keys, keys); values = _dbus_list_get_next_link (&entry->values, values); } @@ -157,38 +263,127 @@ dbus_address_entry_get_value (DBusAddressEntry *entry, return NULL; } +static dbus_bool_t +append_unescaped_value (DBusString *unescaped, + const DBusString *escaped, + int escaped_start, + int escaped_len, + DBusError *error) +{ + const char *p; + const char *end; + dbus_bool_t ret; + + ret = FALSE; + + p = _dbus_string_get_const_data (escaped) + escaped_start; + end = p + escaped_len; + while (p != end) + { + if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p)) + { + if (!_dbus_string_append_byte (unescaped, *p)) + goto out; + } + else if (*p == '%') + { + /* Efficiency is king */ + char buf[3]; + DBusString hex; + int hex_end; + + ++p; + + if ((p + 2) > end) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, percent character was not followed by two hex digits"); + goto out; + } + + buf[0] = *p; + ++p; + buf[1] = *p; + buf[2] = '\0'; + + _dbus_string_init_const (&hex, buf); + + if (!_dbus_string_hex_decode (&hex, 0, &hex_end, + unescaped, + _dbus_string_get_length (unescaped))) + goto out; + + if (hex_end != 2) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, percent character was followed by characters other than hex digits"); + goto out; + } + } + else + { + /* Error, should have been escaped */ + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "In D-Bus address, character '%c' should have been escaped\n", + *p); + goto out; + } + + ++p; + } + + ret = TRUE; + + out: + if (!ret && error && !dbus_error_is_set (error)) + _DBUS_SET_OOM (error); + + _dbus_assert (ret || error == NULL || dbus_error_is_set (error)); + + return ret; +} + /** * Parses an address string of the form: * * method:key=value,key=value;method:key=value * - * @todo document address format in the specification + * See the D-Bus specification for complete docs on the format. * - * @todo need to be able to escape ';' and ',' in the - * key values, and the parsing needs to handle that. + * When connecting to an address, the first address entries + * in the semicolon-separated list should be tried first. * * @param address the address. * @param entry return location to an array of entries. * @param array_len return location for array length. - * @param result return location for result code. + * @param error address where an error can be returned. * @returns #TRUE on success, #FALSE otherwise. */ dbus_bool_t dbus_parse_address (const char *address, DBusAddressEntry ***entry, int *array_len, - DBusResultCode *result) + DBusError *error) { DBusString str; int pos, end_pos, len, i; DBusList *entries, *link; DBusAddressEntry **entry_array; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + _dbus_string_init_const (&str, address); entries = NULL; pos = 0; len = _dbus_string_get_length (&str); + + if (len == 0) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Empty address '%s'", address); + goto error; + } while (pos < len) { @@ -199,7 +394,7 @@ dbus_parse_address (const char *address, entry = create_entry (); if (!entry) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto error; } @@ -207,7 +402,7 @@ dbus_parse_address (const char *address, /* Append the entry */ if (!_dbus_list_append (&entries, entry)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_address_entry_free (entry); goto error; } @@ -219,13 +414,13 @@ dbus_parse_address (const char *address, /* Look for the colon : */ if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos)) { - dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS); + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon"); goto error; } if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto error; } @@ -238,11 +433,12 @@ dbus_parse_address (const char *address, if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos)) comma_pos = end_pos; - if (!_dbus_string_find (&str, pos, "=", &equals_pos) || - equals_pos == pos || equals_pos + 1 == end_pos) + if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) || + equals_pos == pos || equals_pos + 1 == comma_pos) { - dbus_set_result (result, DBUS_RESULT_BAD_ADDRESS); - goto error; + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "'=' character not found or has no value following it"); + goto error; } else { @@ -253,30 +449,30 @@ dbus_parse_address (const char *address, if (!key) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto error; } value = dbus_new0 (DBusString, 1); if (!value) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_free (key); goto error; } - if (!_dbus_string_init (key, _DBUS_INT_MAX)) + if (!_dbus_string_init (key)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); dbus_free (key); dbus_free (value); goto error; } - if (!_dbus_string_init (value, _DBUS_INT_MAX)) + if (!_dbus_string_init (value)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (key); dbus_free (key); @@ -286,7 +482,7 @@ dbus_parse_address (const char *address, if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (key); _dbus_string_free (value); @@ -295,9 +491,10 @@ dbus_parse_address (const char *address, goto error; } - if (!_dbus_string_copy_len (&str, equals_pos + 1, comma_pos - equals_pos - 1, value, 0)) + if (!append_unescaped_value (value, &str, equals_pos + 1, + comma_pos - equals_pos - 1, error)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + _dbus_assert (error == NULL || dbus_error_is_set (error)); _dbus_string_free (key); _dbus_string_free (value); @@ -308,7 +505,7 @@ dbus_parse_address (const char *address, if (!_dbus_list_append (&entry->keys, key)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (key); _dbus_string_free (value); @@ -319,7 +516,7 @@ dbus_parse_address (const char *address, if (!_dbus_list_append (&entry->values, value)) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); _dbus_string_free (value); dbus_free (value); @@ -339,7 +536,7 @@ dbus_parse_address (const char *address, if (!entry_array) { - dbus_set_result (result, DBUS_RESULT_NO_MEMORY); + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto error; } @@ -358,7 +555,6 @@ dbus_parse_address (const char *address, _dbus_list_clear (&entries); *entry = entry_array; - dbus_set_result (result, DBUS_RESULT_SUCCESS); return TRUE; error: @@ -376,21 +572,200 @@ dbus_parse_address (const char *address, } +/** + * Escapes the given string as a value in a key=value pair + * for a D-Bus address. + * + * @param value the unescaped value + * @returns newly-allocated escaped value or #NULL if no memory + */ +char* +dbus_address_escape_value (const char *value) +{ + DBusString escaped; + DBusString unescaped; + char *ret; + + ret = NULL; + + _dbus_string_init_const (&unescaped, value); + + if (!_dbus_string_init (&escaped)) + return NULL; + + if (!_dbus_address_append_escaped (&escaped, &unescaped)) + goto out; + + if (!_dbus_string_steal_data (&escaped, &ret)) + goto out; + + out: + _dbus_string_free (&escaped); + return ret; +} + +/** + * Unescapes the given string as a value in a key=value pair + * for a D-Bus address. Note that dbus_address_entry_get_value() + * returns an already-unescaped value. + * + * @param value the escaped value + * @param error error to set if the unescaping fails + * @returns newly-allocated unescaped value or #NULL if no memory + */ +char* +dbus_address_unescape_value (const char *value, + DBusError *error) +{ + DBusString unescaped; + DBusString escaped; + char *ret; + + ret = NULL; + + _dbus_string_init_const (&escaped, value); + + if (!_dbus_string_init (&unescaped)) + return NULL; + + if (!append_unescaped_value (&unescaped, &escaped, + 0, _dbus_string_get_length (&escaped), + error)) + goto out; + + if (!_dbus_string_steal_data (&unescaped, &ret)) + goto out; -/** @} */ + out: + if (ret == NULL && error && !dbus_error_is_set (error)) + _DBUS_SET_OOM (error); + + _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error)); + + _dbus_string_free (&unescaped); + return ret; +} + +/** @} */ /* End of public API */ + +#ifdef DBUS_ENABLE_EMBEDDED_TESTS + +#ifndef DOXYGEN_SHOULD_SKIP_THIS -#ifdef DBUS_BUILD_TESTS #include "dbus-test.h" +#include + +typedef struct +{ + const char *escaped; + const char *unescaped; +} EscapeTest; + +static const EscapeTest escape_tests[] = { + { "abcde", "abcde" }, + { "", "" }, + { "%20%20", " " }, + { "%24", "$" }, + { "%25", "%" }, + { "abc%24", "abc$" }, + { "%24abc", "$abc" }, + { "abc%24abc", "abc$abc" }, + { "/", "/" }, + { "-", "-" }, + { "_", "_" }, + { "A", "A" }, + { "I", "I" }, + { "Z", "Z" }, + { "a", "a" }, + { "i", "i" }, + { "z", "z" }, + /* Bug: https://bugs.freedesktop.org/show_bug.cgi?id=53499 */ + { "%c3%b6", "\xc3\xb6" } +}; + +static const char* invalid_escaped_values[] = { + "%a", + "%q", + "%az", + "%%", + "%$$", + "abc%a", + "%axyz", + "%", + "$", + " ", +}; dbus_bool_t _dbus_address_test (void) { DBusAddressEntry **entries; - int len; - DBusResultCode result; + int len; + DBusError error = DBUS_ERROR_INIT; + int i; + i = 0; + while (i < _DBUS_N_ELEMENTS (escape_tests)) + { + const EscapeTest *test = &escape_tests[i]; + char *escaped; + char *unescaped; + + escaped = dbus_address_escape_value (test->unescaped); + if (escaped == NULL) + _dbus_assert_not_reached ("oom"); + + if (strcmp (escaped, test->escaped) != 0) + { + _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n", + test->unescaped, escaped, test->escaped); + exit (1); + } + dbus_free (escaped); + + unescaped = dbus_address_unescape_value (test->escaped, &error); + if (unescaped == NULL) + { + _dbus_warn ("Failed to unescape '%s': %s\n", + test->escaped, error.message); + dbus_error_free (&error); + exit (1); + } + + if (strcmp (unescaped, test->unescaped) != 0) + { + _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n", + test->escaped, unescaped, test->unescaped); + exit (1); + } + dbus_free (unescaped); + + ++i; + } + + i = 0; + while (i < _DBUS_N_ELEMENTS (invalid_escaped_values)) + { + char *unescaped; + + unescaped = dbus_address_unescape_value (invalid_escaped_values[i], + &error); + if (unescaped != NULL) + { + _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n", + invalid_escaped_values[i], unescaped); + dbus_free (unescaped); + exit (1); + } + + _dbus_assert (dbus_error_is_set (&error)); + dbus_error_free (&error); + + ++i; + } + if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;", - &entries, &len, &result)) + &entries, &len, &error)) _dbus_assert_not_reached ("could not parse address"); _dbus_assert (len == 2); _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0); @@ -400,28 +775,54 @@ _dbus_address_test (void) dbus_address_entries_free (entries); /* Different possible errors */ - if (dbus_parse_address ("foo", &entries, &len, &result)) + if (dbus_parse_address ("", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); - if (dbus_parse_address ("foo:bar", &entries, &len, &result)) + if (dbus_parse_address ("foo", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); - - if (dbus_parse_address ("foo:bar,baz", &entries, &len, &result)) + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); - - if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &result)) + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); - - if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &result)) + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); - - if (dbus_parse_address ("foo:=foo", &entries, &len, &result)) + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); - - if (dbus_parse_address ("foo:foo=", &entries, &len, &result)) + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:=foo", &entries, &len, &error)) _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + if (dbus_parse_address ("foo:foo=", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + + if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error)) + _dbus_assert_not_reached ("Parsed incorrect address."); + else + dbus_error_free (&error); + return TRUE; } +#endif /* !DOXYGEN_SHOULD_SKIP_THIS */ + #endif