dbus-address.c

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

Generated on Tue Oct 14 03:28:52 2008 for D-Bus by  doxygen 1.5.3