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   while (pos < len)
00381     {
00382       DBusAddressEntry *entry;
00383 
00384       int found_pos;
00385 
00386       entry = create_entry ();
00387       if (!entry)
00388         {
00389           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00390 
00391           goto error;
00392         }
00393       
00394       /* Append the entry */
00395       if (!_dbus_list_append (&entries, entry))
00396         {
00397           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00398           dbus_address_entry_free (entry);
00399           goto error;
00400         }
00401       
00402       /* Look for a semi-colon */
00403       if (!_dbus_string_find (&str, pos, ";", &end_pos))
00404         end_pos = len;
00405       
00406       /* Look for the colon : */
00407       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
00408         {
00409           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
00410           goto error;
00411         }
00412 
00413       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
00414         {
00415           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00416           goto error;
00417         }
00418           
00419       pos = found_pos + 1;
00420 
00421       while (pos < end_pos)
00422         {
00423           int comma_pos, equals_pos;
00424 
00425           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
00426             comma_pos = end_pos;
00427           
00428           if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
00429               equals_pos == pos || equals_pos + 1 == comma_pos)
00430             {
00431               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
00432                               "'=' character not found or has no value following it");
00433               goto error;
00434             }
00435           else
00436             {
00437               DBusString *key;
00438               DBusString *value;
00439 
00440               key = dbus_new0 (DBusString, 1);
00441 
00442               if (!key)
00443                 {
00444                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00445                   goto error;
00446                 }
00447 
00448               value = dbus_new0 (DBusString, 1);
00449               if (!value)
00450                 {
00451                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00452                   dbus_free (key);
00453                   goto error;
00454                 }
00455               
00456               if (!_dbus_string_init (key))
00457                 {
00458                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00459                   dbus_free (key);
00460                   dbus_free (value);
00461                   
00462                   goto error;
00463                 }
00464               
00465               if (!_dbus_string_init (value))
00466                 {
00467                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00468                   _dbus_string_free (key);
00469 
00470                   dbus_free (key);
00471                   dbus_free (value);              
00472                   goto error;
00473                 }
00474 
00475               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
00476                 {
00477                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00478                   _dbus_string_free (key);
00479                   _dbus_string_free (value);
00480 
00481                   dbus_free (key);
00482                   dbus_free (value);              
00483                   goto error;
00484                 }
00485 
00486               if (!append_unescaped_value (value, &str, equals_pos + 1,
00487                                            comma_pos - equals_pos - 1, error))
00488                 {
00489                   _dbus_assert (error == NULL || dbus_error_is_set (error));
00490                   _dbus_string_free (key);
00491                   _dbus_string_free (value);
00492 
00493                   dbus_free (key);
00494                   dbus_free (value);              
00495                   goto error;
00496                 }
00497 
00498               if (!_dbus_list_append (&entry->keys, key))
00499                 {
00500                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00501                   _dbus_string_free (key);
00502                   _dbus_string_free (value);
00503 
00504                   dbus_free (key);
00505                   dbus_free (value);              
00506                   goto error;
00507                 }
00508 
00509               if (!_dbus_list_append (&entry->values, value))
00510                 {
00511                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00512                   _dbus_string_free (value);
00513 
00514                   dbus_free (value);
00515                   goto error;             
00516                 }
00517             }
00518 
00519           pos = comma_pos + 1;
00520         }
00521 
00522       pos = end_pos + 1;
00523     }
00524 
00525   *array_len = _dbus_list_get_length (&entries);
00526   
00527   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
00528 
00529   if (!entry_array)
00530     {
00531       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00532       
00533       goto error;
00534     }
00535   
00536   entry_array [*array_len] = NULL;
00537 
00538   link = _dbus_list_get_first_link (&entries);
00539   i = 0;
00540   while (link != NULL)
00541     {
00542       entry_array[i] = link->data;
00543       i++;
00544       link = _dbus_list_get_next_link (&entries, link);
00545     }
00546 
00547   _dbus_list_clear (&entries);
00548   *entry = entry_array;
00549 
00550   return TRUE;
00551 
00552  error:
00553   
00554   link = _dbus_list_get_first_link (&entries);
00555   while (link != NULL)
00556     {
00557       dbus_address_entry_free (link->data);
00558       link = _dbus_list_get_next_link (&entries, link);
00559     }
00560 
00561   _dbus_list_clear (&entries);
00562   
00563   return FALSE;
00564   
00565 }
00566 
00574 char*
00575 dbus_address_escape_value (const char *value)
00576 {
00577   DBusString escaped;
00578   DBusString unescaped;
00579   char *ret;
00580 
00581   ret = NULL;
00582 
00583   _dbus_string_init_const (&unescaped, value);
00584   
00585   if (!_dbus_string_init (&escaped))
00586     return NULL;
00587 
00588   if (!_dbus_address_append_escaped (&escaped, &unescaped))
00589     goto out;
00590   
00591   if (!_dbus_string_steal_data (&escaped, &ret))
00592     goto out;
00593 
00594  out:
00595   _dbus_string_free (&escaped);
00596   return ret;
00597 }
00598 
00608 char*
00609 dbus_address_unescape_value (const char *value,
00610                              DBusError  *error)
00611 {
00612   DBusString unescaped;
00613   DBusString escaped;
00614   char *ret;
00615   
00616   ret = NULL;
00617 
00618   _dbus_string_init_const (&escaped, value);
00619   
00620   if (!_dbus_string_init (&unescaped))
00621     return NULL;
00622 
00623   if (!append_unescaped_value (&unescaped, &escaped,
00624                                0, _dbus_string_get_length (&escaped),
00625                                error))
00626     goto out;
00627   
00628   if (!_dbus_string_steal_data (&unescaped, &ret))
00629     goto out;
00630 
00631  out:
00632   if (ret == NULL && error && !dbus_error_is_set (error))
00633     _DBUS_SET_OOM (error);
00634 
00635   _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
00636   
00637   _dbus_string_free (&unescaped);
00638   return ret;
00639 }
00640  /* End of public API */
00642 
00643 #ifdef DBUS_BUILD_TESTS
00644 
00645 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00646 
00647 #include "dbus-test.h"
00648 #include <stdlib.h>
00649 
00650 typedef struct
00651 {
00652   const char *escaped;
00653   const char *unescaped;
00654 } EscapeTest;
00655 
00656 static const EscapeTest escape_tests[] = {
00657   { "abcde", "abcde" },
00658   { "", "" },
00659   { "%20%20", "  " },
00660   { "%24", "$" },
00661   { "%25", "%" },
00662   { "abc%24", "abc$" },
00663   { "%24abc", "$abc" },
00664   { "abc%24abc", "abc$abc" },
00665   { "/", "/" },
00666   { "-", "-" },
00667   { "_", "_" },
00668   { "A", "A" },
00669   { "I", "I" },
00670   { "Z", "Z" },
00671   { "a", "a" },
00672   { "i", "i" },
00673   { "z", "z" }
00674 };
00675 
00676 static const char* invalid_escaped_values[] = {
00677   "%a",
00678   "%q",
00679   "%az",
00680   "%%",
00681   "%$$",
00682   "abc%a",
00683   "%axyz",
00684   "%",
00685   "$",
00686   " ",
00687   "*"
00688 };
00689 
00690 dbus_bool_t
00691 _dbus_address_test (void)
00692 {
00693   DBusAddressEntry **entries;
00694   int len;  
00695   DBusError error;
00696   int i;
00697 
00698   dbus_error_init (&error);
00699 
00700   i = 0;
00701   while (i < _DBUS_N_ELEMENTS (escape_tests))
00702     {
00703       const EscapeTest *test = &escape_tests[i];
00704       char *escaped;
00705       char *unescaped;
00706 
00707       escaped = dbus_address_escape_value (test->unescaped);
00708       if (escaped == NULL)
00709         _dbus_assert_not_reached ("oom");
00710 
00711       if (strcmp (escaped, test->escaped) != 0)
00712         {
00713           _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
00714                       test->unescaped, escaped, test->escaped);
00715           exit (1);
00716         }
00717       dbus_free (escaped);
00718 
00719       unescaped = dbus_address_unescape_value (test->escaped, &error);
00720       if (unescaped == NULL)
00721         {
00722           _dbus_warn ("Failed to unescape '%s': %s\n",
00723                       test->escaped, error.message);
00724           dbus_error_free (&error);
00725           exit (1);
00726         }
00727 
00728       if (strcmp (unescaped, test->unescaped) != 0)
00729         {
00730           _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
00731                       test->escaped, unescaped, test->unescaped);
00732           exit (1);
00733         }
00734       dbus_free (unescaped);
00735       
00736       ++i;
00737     }
00738 
00739   i = 0;
00740   while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
00741     {
00742       char *unescaped;
00743 
00744       unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
00745                                                &error);
00746       if (unescaped != NULL)
00747         {
00748           _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
00749                       invalid_escaped_values[i], unescaped);
00750           dbus_free (unescaped);
00751           exit (1);
00752         }
00753 
00754       _dbus_assert (dbus_error_is_set (&error));
00755       dbus_error_free (&error);
00756 
00757       ++i;
00758     }
00759   
00760   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
00761                            &entries, &len, &error))
00762     _dbus_assert_not_reached ("could not parse address");
00763   _dbus_assert (len == 2);
00764   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
00765   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
00766   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
00767   
00768   dbus_address_entries_free (entries);
00769 
00770   /* Different possible errors */
00771   if (dbus_parse_address ("foo", &entries, &len, &error))
00772     _dbus_assert_not_reached ("Parsed incorrect address.");
00773   else
00774     dbus_error_free (&error);
00775   
00776   if (dbus_parse_address ("foo:bar", &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:bar,baz", &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=foo,baz", &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=foo;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:=foo", &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:foo=", &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,bar=baz", &entries, &len, &error))
00807     _dbus_assert_not_reached ("Parsed incorrect address.");
00808   else
00809     dbus_error_free (&error);
00810 
00811   return TRUE;
00812 }
00813 
00814 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
00815 
00816 #endif

Generated on Tue Apr 15 15:53:54 2008 for D-Bus by  doxygen 1.5.1