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  /* End of internals */
00052 
00053 static void
00054 dbus_address_entry_free (DBusAddressEntry *entry)
00055 {
00056   DBusList *link;
00057   
00058   _dbus_string_free (&entry->method);
00059 
00060   link = _dbus_list_get_first_link (&entry->keys);
00061   while (link != NULL)
00062     {
00063       _dbus_string_free (link->data);
00064       dbus_free (link->data);
00065       
00066       link = _dbus_list_get_next_link (&entry->keys, link);
00067     }
00068   _dbus_list_clear (&entry->keys);
00069   
00070   link = _dbus_list_get_first_link (&entry->values);
00071   while (link != NULL)
00072     {
00073       _dbus_string_free (link->data);
00074       dbus_free (link->data);
00075       
00076       link = _dbus_list_get_next_link (&entry->values, link);
00077     }
00078   _dbus_list_clear (&entry->values);
00079   
00080   dbus_free (entry);
00081 }
00082 
00096 void
00097 dbus_address_entries_free (DBusAddressEntry **entries)
00098 {
00099   int i;
00100   
00101   for (i = 0; entries[i] != NULL; i++)
00102     dbus_address_entry_free (entries[i]);
00103   dbus_free (entries);
00104 }
00105 
00106 static DBusAddressEntry *
00107 create_entry (void)
00108 {
00109   DBusAddressEntry *entry;
00110 
00111   entry = dbus_new0 (DBusAddressEntry, 1);
00112 
00113   if (entry == NULL)
00114     return NULL;
00115 
00116   if (!_dbus_string_init (&entry->method))
00117     {
00118       dbus_free (entry);
00119       return NULL;
00120     }
00121 
00122   return entry;
00123 }
00124 
00132 const char *
00133 dbus_address_entry_get_method (DBusAddressEntry *entry)
00134 {
00135   return _dbus_string_get_const_data (&entry->method);
00136 }
00137 
00145 const char *
00146 dbus_address_entry_get_value (DBusAddressEntry *entry,
00147                               const char       *key)
00148 {
00149   DBusList *values, *keys;
00150 
00151   keys = _dbus_list_get_first_link (&entry->keys);
00152   values = _dbus_list_get_first_link (&entry->values);
00153 
00154   while (keys != NULL)
00155     {
00156       _dbus_assert (values != NULL);
00157 
00158       if (_dbus_string_equal_c_str (keys->data, key))
00159         return _dbus_string_get_const_data (values->data);
00160 
00161       keys = _dbus_list_get_next_link (&entry->keys, keys);
00162       values = _dbus_list_get_next_link (&entry->values, values);
00163     }
00164   
00165   return NULL;
00166 }
00167 
00168 #define _DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE(b)        \
00169          (((b) >= 'a' && (b) <= 'z') ||                 \
00170           ((b) >= 'A' && (b) <= 'Z') ||                 \
00171           ((b) >= '0' && (b) <= '9') ||                 \
00172           (b) == '-' ||                                 \
00173           (b) == '_' ||                                 \
00174           (b) == '/' ||                                 \
00175           (b) == '\\' ||                                \
00176           (b) == '.')
00177 
00186 dbus_bool_t
00187 _dbus_address_append_escaped (DBusString       *escaped,
00188                               const DBusString *unescaped)
00189 {
00190   const char *p;
00191   const char *end;
00192   dbus_bool_t ret;
00193   int orig_len;
00194 
00195   ret = FALSE;
00196 
00197   orig_len = _dbus_string_get_length (escaped);
00198   p = _dbus_string_get_const_data (unescaped);
00199   end = p + _dbus_string_get_length (unescaped);
00200   while (p != end)
00201     {
00202       if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
00203         {
00204           if (!_dbus_string_append_byte (escaped, *p))
00205             goto out;
00206         }
00207       else
00208         {
00209           if (!_dbus_string_append_byte (escaped, '%'))
00210             goto out;
00211           if (!_dbus_string_append_byte_as_hex (escaped, *p))
00212             goto out;
00213         }
00214       
00215       ++p;
00216     }
00217 
00218   ret = TRUE;
00219   
00220  out:
00221   if (!ret)
00222     _dbus_string_set_length (escaped, orig_len);
00223   return ret;
00224 }
00225 
00226 static dbus_bool_t
00227 append_unescaped_value (DBusString       *unescaped,
00228                         const DBusString *escaped,
00229                         int               escaped_start,
00230                         int               escaped_len,
00231                         DBusError        *error)
00232 {
00233   const char *p;
00234   const char *end;
00235   dbus_bool_t ret;
00236   
00237   ret = FALSE;
00238 
00239   p = _dbus_string_get_const_data (escaped) + escaped_start;
00240   end = p + escaped_len;
00241   while (p != end)
00242     {
00243       if (_DBUS_ADDRESS_OPTIONALLY_ESCAPED_BYTE (*p))
00244         {
00245           if (!_dbus_string_append_byte (unescaped, *p))
00246             goto out;
00247         }
00248       else if (*p == '%')
00249         {
00250           /* Efficiency is king */
00251           char buf[3];
00252           DBusString hex;
00253           int hex_end;
00254           
00255           ++p;
00256 
00257           if ((p + 2) > end)
00258             {
00259               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
00260                               "In D-BUS address, percent character was not followed by two hex digits");
00261               goto out;
00262             }
00263             
00264           buf[0] = *p;
00265           ++p;
00266           buf[1] = *p;
00267           buf[2] = '\0';
00268 
00269           _dbus_string_init_const (&hex, buf);
00270 
00271           if (!_dbus_string_hex_decode (&hex, 0, &hex_end,
00272                                         unescaped,
00273                                         _dbus_string_get_length (unescaped)))
00274             goto out;
00275 
00276           if (hex_end != 2)
00277             {
00278               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
00279                               "In D-BUS address, percent character was followed by characters other than hex digits");
00280               goto out;
00281             }
00282         }
00283       else
00284         {
00285           /* Error, should have been escaped */
00286           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
00287                           "In D-BUS address, character '%c' should have been escaped\n",
00288                           *p);
00289           goto out;
00290         }
00291       
00292       ++p;
00293     }
00294 
00295   ret = TRUE;
00296   
00297  out:
00298   if (!ret && error && !dbus_error_is_set (error))
00299     _DBUS_SET_OOM (error);
00300 
00301   _dbus_assert (ret || error == NULL || dbus_error_is_set (error));
00302   
00303   return ret;
00304 }
00305 
00319 dbus_bool_t
00320 dbus_parse_address (const char         *address,
00321                     DBusAddressEntry ***entry,
00322                     int                *array_len,
00323                     DBusError          *error)
00324 {
00325   DBusString str;
00326   int pos, end_pos, len, i;
00327   DBusList *entries, *link;
00328   DBusAddressEntry **entry_array;
00329 
00330   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00331   
00332   _dbus_string_init_const (&str, address);
00333 
00334   entries = NULL;
00335   pos = 0;
00336   len = _dbus_string_get_length (&str);
00337   
00338   while (pos < len)
00339     {
00340       DBusAddressEntry *entry;
00341 
00342       int found_pos;
00343 
00344       entry = create_entry ();
00345       if (!entry)
00346         {
00347           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00348 
00349           goto error;
00350         }
00351       
00352       /* Append the entry */
00353       if (!_dbus_list_append (&entries, entry))
00354         {
00355           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00356           dbus_address_entry_free (entry);
00357           goto error;
00358         }
00359       
00360       /* Look for a semi-colon */
00361       if (!_dbus_string_find (&str, pos, ";", &end_pos))
00362         end_pos = len;
00363       
00364       /* Look for the colon : */
00365       if (!_dbus_string_find_to (&str, pos, end_pos, ":", &found_pos))
00366         {
00367           dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, "Address does not contain a colon");
00368           goto error;
00369         }
00370 
00371       if (!_dbus_string_copy_len (&str, pos, found_pos - pos, &entry->method, 0))
00372         {
00373           dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00374           goto error;
00375         }
00376           
00377       pos = found_pos + 1;
00378 
00379       while (pos < end_pos)
00380         {
00381           int comma_pos, equals_pos;
00382 
00383           if (!_dbus_string_find_to (&str, pos, end_pos, ",", &comma_pos))
00384             comma_pos = end_pos;
00385           
00386           if (!_dbus_string_find_to (&str, pos, comma_pos, "=", &equals_pos) ||
00387               equals_pos == pos || equals_pos + 1 == comma_pos)
00388             {
00389               dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
00390                               "'=' character not found or has no value following it");
00391               goto error;
00392             }
00393           else
00394             {
00395               DBusString *key;
00396               DBusString *value;
00397 
00398               key = dbus_new0 (DBusString, 1);
00399 
00400               if (!key)
00401                 {
00402                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00403                   goto error;
00404                 }
00405 
00406               value = dbus_new0 (DBusString, 1);
00407               if (!value)
00408                 {
00409                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00410                   dbus_free (key);
00411                   goto error;
00412                 }
00413               
00414               if (!_dbus_string_init (key))
00415                 {
00416                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00417                   dbus_free (key);
00418                   dbus_free (value);
00419                   
00420                   goto error;
00421                 }
00422               
00423               if (!_dbus_string_init (value))
00424                 {
00425                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00426                   _dbus_string_free (key);
00427 
00428                   dbus_free (key);
00429                   dbus_free (value);              
00430                   goto error;
00431                 }
00432 
00433               if (!_dbus_string_copy_len (&str, pos, equals_pos - pos, key, 0))
00434                 {
00435                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00436                   _dbus_string_free (key);
00437                   _dbus_string_free (value);
00438 
00439                   dbus_free (key);
00440                   dbus_free (value);              
00441                   goto error;
00442                 }
00443 
00444               if (!append_unescaped_value (value, &str, equals_pos + 1,
00445                                            comma_pos - equals_pos - 1, error))
00446                 {
00447                   _dbus_assert (error == NULL || dbus_error_is_set (error));
00448                   _dbus_string_free (key);
00449                   _dbus_string_free (value);
00450 
00451                   dbus_free (key);
00452                   dbus_free (value);              
00453                   goto error;
00454                 }
00455 
00456               if (!_dbus_list_append (&entry->keys, key))
00457                 {
00458                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00459                   _dbus_string_free (key);
00460                   _dbus_string_free (value);
00461 
00462                   dbus_free (key);
00463                   dbus_free (value);              
00464                   goto error;
00465                 }
00466 
00467               if (!_dbus_list_append (&entry->values, value))
00468                 {
00469                   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);             
00470                   _dbus_string_free (value);
00471 
00472                   dbus_free (value);
00473                   goto error;             
00474                 }
00475             }
00476 
00477           pos = comma_pos + 1;
00478         }
00479 
00480       pos = end_pos + 1;
00481     }
00482 
00483   *array_len = _dbus_list_get_length (&entries);
00484   
00485   entry_array = dbus_new (DBusAddressEntry *, *array_len + 1);
00486 
00487   if (!entry_array)
00488     {
00489       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
00490       
00491       goto error;
00492     }
00493   
00494   entry_array [*array_len] = NULL;
00495 
00496   link = _dbus_list_get_first_link (&entries);
00497   i = 0;
00498   while (link != NULL)
00499     {
00500       entry_array[i] = link->data;
00501       i++;
00502       link = _dbus_list_get_next_link (&entries, link);
00503     }
00504 
00505   _dbus_list_clear (&entries);
00506   *entry = entry_array;
00507 
00508   return TRUE;
00509 
00510  error:
00511   
00512   link = _dbus_list_get_first_link (&entries);
00513   while (link != NULL)
00514     {
00515       dbus_address_entry_free (link->data);
00516       link = _dbus_list_get_next_link (&entries, link);
00517     }
00518 
00519   _dbus_list_clear (&entries);
00520   
00521   return FALSE;
00522   
00523 }
00524 
00532 char*
00533 dbus_address_escape_value (const char *value)
00534 {
00535   DBusString escaped;
00536   DBusString unescaped;
00537   char *ret;
00538 
00539   ret = NULL;
00540 
00541   _dbus_string_init_const (&unescaped, value);
00542   
00543   if (!_dbus_string_init (&escaped))
00544     return NULL;
00545 
00546   if (!_dbus_address_append_escaped (&escaped, &unescaped))
00547     goto out;
00548   
00549   if (!_dbus_string_steal_data (&escaped, &ret))
00550     goto out;
00551 
00552  out:
00553   _dbus_string_free (&escaped);
00554   return ret;
00555 }
00556 
00565 char*
00566 dbus_address_unescape_value (const char *value,
00567                              DBusError  *error)
00568 {
00569   DBusString unescaped;
00570   DBusString escaped;
00571   char *ret;
00572   
00573   ret = NULL;
00574 
00575   _dbus_string_init_const (&escaped, value);
00576   
00577   if (!_dbus_string_init (&unescaped))
00578     return NULL;
00579 
00580   if (!append_unescaped_value (&unescaped, &escaped,
00581                                0, _dbus_string_get_length (&escaped),
00582                                error))
00583     goto out;
00584   
00585   if (!_dbus_string_steal_data (&unescaped, &ret))
00586     goto out;
00587 
00588  out:
00589   if (ret == NULL && error && !dbus_error_is_set (error))
00590     _DBUS_SET_OOM (error);
00591 
00592   _dbus_assert (ret != NULL || error == NULL || dbus_error_is_set (error));
00593   
00594   _dbus_string_free (&unescaped);
00595   return ret;
00596 }
00597  /* End of public API */
00599 
00600 #ifdef DBUS_BUILD_TESTS
00601 #include "dbus-test.h"
00602 #include <stdlib.h>
00603 
00604 typedef struct
00605 {
00606   const char *escaped;
00607   const char *unescaped;
00608 } EscapeTest;
00609 
00610 static const EscapeTest escape_tests[] = {
00611   { "abcde", "abcde" },
00612   { "", "" },
00613   { "%20%20", "  " },
00614   { "%24", "$" },
00615   { "%25", "%" },
00616   { "abc%24", "abc$" },
00617   { "%24abc", "$abc" },
00618   { "abc%24abc", "abc$abc" },
00619   { "/", "/" },
00620   { "-", "-" },
00621   { "_", "_" },
00622   { "A", "A" },
00623   { "I", "I" },
00624   { "Z", "Z" },
00625   { "a", "a" },
00626   { "i", "i" },
00627   { "z", "z" }
00628 };
00629 
00630 static const char* invalid_escaped_values[] = {
00631   "%a",
00632   "%q",
00633   "%az",
00634   "%%",
00635   "%$$",
00636   "abc%a",
00637   "%axyz",
00638   "%",
00639   "$",
00640   " ",
00641   "*"
00642 };
00643 
00644 dbus_bool_t
00645 _dbus_address_test (void)
00646 {
00647   DBusAddressEntry **entries;
00648   int len;  
00649   DBusError error;
00650   int i;
00651 
00652   dbus_error_init (&error);
00653 
00654   i = 0;
00655   while (i < _DBUS_N_ELEMENTS (escape_tests))
00656     {
00657       const EscapeTest *test = &escape_tests[i];
00658       char *escaped;
00659       char *unescaped;
00660 
00661       escaped = dbus_address_escape_value (test->unescaped);
00662       if (escaped == NULL)
00663         _dbus_assert_not_reached ("oom");
00664 
00665       if (strcmp (escaped, test->escaped) != 0)
00666         {
00667           _dbus_warn ("Escaped '%s' as '%s' should have been '%s'\n",
00668                       test->unescaped, escaped, test->escaped);
00669           exit (1);
00670         }
00671       dbus_free (escaped);
00672 
00673       unescaped = dbus_address_unescape_value (test->escaped, &error);
00674       if (unescaped == NULL)
00675         {
00676           _dbus_warn ("Failed to unescape '%s': %s\n",
00677                       test->escaped, error.message);
00678           dbus_error_free (&error);
00679           exit (1);
00680         }
00681 
00682       if (strcmp (unescaped, test->unescaped) != 0)
00683         {
00684           _dbus_warn ("Unescaped '%s' as '%s' should have been '%s'\n",
00685                       test->escaped, unescaped, test->unescaped);
00686           exit (1);
00687         }
00688       dbus_free (unescaped);
00689       
00690       ++i;
00691     }
00692 
00693   i = 0;
00694   while (i < _DBUS_N_ELEMENTS (invalid_escaped_values))
00695     {
00696       char *unescaped;
00697 
00698       unescaped = dbus_address_unescape_value (invalid_escaped_values[i],
00699                                                &error);
00700       if (unescaped != NULL)
00701         {
00702           _dbus_warn ("Should not have successfully unescaped '%s' to '%s'\n",
00703                       invalid_escaped_values[i], unescaped);
00704           dbus_free (unescaped);
00705           exit (1);
00706         }
00707 
00708       _dbus_assert (dbus_error_is_set (&error));
00709       dbus_error_free (&error);
00710 
00711       ++i;
00712     }
00713   
00714   if (!dbus_parse_address ("unix:path=/tmp/foo;debug:name=test,sliff=sloff;",
00715                            &entries, &len, &error))
00716     _dbus_assert_not_reached ("could not parse address");
00717   _dbus_assert (len == 2);
00718   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[0], "path"), "/tmp/foo") == 0);
00719   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "name"), "test") == 0);
00720   _dbus_assert (strcmp (dbus_address_entry_get_value (entries[1], "sliff"), "sloff") == 0);
00721   
00722   dbus_address_entries_free (entries);
00723 
00724   /* Different possible errors */
00725   if (dbus_parse_address ("foo", &entries, &len, &error))
00726     _dbus_assert_not_reached ("Parsed incorrect address.");
00727   else
00728     dbus_error_free (&error);
00729   
00730   if (dbus_parse_address ("foo:bar", &entries, &len, &error))
00731     _dbus_assert_not_reached ("Parsed incorrect address.");
00732   else
00733     dbus_error_free (&error);
00734   
00735   if (dbus_parse_address ("foo:bar,baz", &entries, &len, &error))
00736     _dbus_assert_not_reached ("Parsed incorrect address.");
00737   else
00738     dbus_error_free (&error);
00739   
00740   if (dbus_parse_address ("foo:bar=foo,baz", &entries, &len, &error))
00741     _dbus_assert_not_reached ("Parsed incorrect address.");
00742   else
00743     dbus_error_free (&error);
00744   
00745   if (dbus_parse_address ("foo:bar=foo;baz", &entries, &len, &error))
00746     _dbus_assert_not_reached ("Parsed incorrect address.");
00747   else
00748     dbus_error_free (&error);
00749   
00750   if (dbus_parse_address ("foo:=foo", &entries, &len, &error))
00751     _dbus_assert_not_reached ("Parsed incorrect address.");
00752   else
00753     dbus_error_free (&error);
00754   
00755   if (dbus_parse_address ("foo:foo=", &entries, &len, &error))
00756     _dbus_assert_not_reached ("Parsed incorrect address.");
00757   else
00758     dbus_error_free (&error);
00759 
00760   if (dbus_parse_address ("foo:foo,bar=baz", &entries, &len, &error))
00761     _dbus_assert_not_reached ("Parsed incorrect address.");
00762   else
00763     dbus_error_free (&error);
00764 
00765   return TRUE;
00766 }
00767 
00768 #endif

Generated on Tue Jul 7 15:13:58 2009 for D-BUS by  doxygen 1.4.6