dbus-address.c

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., 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           (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

Generated on Thu Jan 22 16:37:04 2009 for D-Bus by  doxygen 1.5.1