New test.
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / LdapDN.cs
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc.  www.novell.com
4
5 * Permission is hereby granted, free of charge, to any person obtaining  a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including  without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
9 * copies of the Software, and to  permit persons to whom the Software is 
10 * furnished to do so, subject to the following conditions:
11
12 * The above copyright notice and this permission notice shall be included in 
13 * all copies or substantial portions of the Software.
14
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *******************************************************************************/
23 //
24 // Novell.Directory.Ldap.LdapDN.cs
25 //
26 // Author:
27 //   Sunil Kumar (Sunilk@novell.com)
28 //
29 // (C) 2003 Novell, Inc (http://www.novell.com)
30 //
31
32 using System;
33 using  Novell.Directory.Ldap.Utilclass;
34
35 namespace Novell.Directory.Ldap
36 {
37         
38         /// <summary>  A utility class to facilitate composition and deomposition
39         /// of distinguished names DNs.
40         /// 
41         /// Specifies methods for manipulating a distinguished name DN
42         /// and a relative distinguished name RDN.
43         /// </summary>
44         public class LdapDN
45         {
46                 /// <summary> Compares the two strings per the distinguishedNameMatch equality matching
47                 /// (using case-ignore matching).  IllegalArgumentException is thrown if one
48                 /// or both DNs are invalid.  UnsupportedOpersationException is thrown if the
49                 /// API implementation is not able to detemine if the DNs match or not.
50                 /// 
51                 /// </summary>
52                 /// <param name="dn1">           String form of the first DN to compare.
53                 /// 
54                 /// </param>
55                 /// <param name="dn2">           String form of the second DN to compare.
56                 /// 
57                 /// </param>
58                 /// <returns> Returns true if the two strings correspond to the same DN; false
59                 /// if the DNs are different.
60                 /// </returns>
61                 [CLSCompliantAttribute(false)]          
62                 public static bool equals(System.String dn1, System.String dn2)
63                 {
64                         DN dnA = new DN(dn1);
65                         DN dnB = new DN(dn2);
66                         return dnA.Equals(dnB);
67                 }
68                 
69                 /// <summary> Returns the RDN after escaping the characters requiring escaping.
70                 /// 
71                 /// For example, for the rdn "cn=Acme, Inc", the escapeRDN method
72                 /// returns "cn=Acme\, Inc".
73                 /// 
74                 /// escapeRDN escapes the AttributeValue by inserting '\' before the
75                 /// following chars: * ',' '+' '"' '\' 'LESSTHAN' 'GREATERTHAN' ';' 
76                 /// '#' if it comes at the beginning of the string, and 
77                 /// ' ' (space) if it comes at the beginning or the end of a string.
78                 /// Note that single-valued attributes can be used because of ambiguity. See
79                 /// RFC 2253 
80                 /// 
81                 /// </summary>
82                 /// <param name="rdn">           The RDN to escape.
83                 /// 
84                 /// </param>
85                 /// <returns> The RDN with escaping characters.
86                 /// </returns>
87                 public static System.String escapeRDN(System.String rdn)
88                 {
89                         System.Text.StringBuilder escapedS = new System.Text.StringBuilder(rdn);
90                         int i = 0;
91                         
92                         while (i < escapedS.Length && escapedS[i] != '=')
93                         {
94                                 i++; //advance until we find the separator =
95                         }
96                         if (i == escapedS.Length)
97                         {
98                                 throw new System.ArgumentException("Could not parse RDN: Attribute " + "type and name must be separated by an equal symbol, '='");
99                         }
100                         
101                         i++;
102                         //check for a space or # at the beginning of a string.
103                         if ((escapedS[i] == ' ') || (escapedS[i] == '#'))
104                         {
105                                 escapedS.Insert(i++, '\\');
106                         }
107                         
108                         //loop from second char to the second to last
109                         for (; i < escapedS.Length; i++)
110                         {
111                                 if ((escapedS[i] == ',') || (escapedS[i] == '+') || (escapedS[i] == '"') || (escapedS[i] == '\\') || (escapedS[i] == '<') || (escapedS[i] == '>') || (escapedS[i] == ';'))
112                                 {
113                                         escapedS.Insert(i++, '\\');
114                                 }
115                         }
116                         
117                         //check last char for a space
118                         if (escapedS[escapedS.Length - 1] == ' ')
119                         {
120                                 escapedS.Insert(escapedS.Length - 1, '\\');
121                         }
122                         return escapedS.ToString();
123                 }
124                 
125                 
126                 
127                 /// <summary> Returns the individual components of a distinguished name (DN).
128                 /// 
129                 /// </summary>
130                 /// <param name="dn">       The distinguished name, for example, "cn=Babs
131                 /// Jensen,ou=Accounting,o=Acme,c=US"
132                 /// 
133                 /// </param>
134                 /// <param name="noTypes">  If true, returns only the values of the
135                 /// components and not the names.  For example, "Babs
136                 /// Jensen", "Accounting", "Acme", "US" instead of
137                 /// "cn=Babs Jensen", "ou=Accounting", "o=Acme", and
138                 /// "c=US".
139                 /// 
140                 /// </param>
141                 /// <returns> An array of strings representing the individual components
142                 /// of a DN, or null if the DN is not valid.
143                 /// </returns>
144                 public static System.String[] explodeDN(System.String dn, bool noTypes)
145                 {
146                         DN dnToExplode = new DN(dn);
147                         return dnToExplode.explodeDN(noTypes);
148                 }
149                 
150                 /// <summary> Returns the individual components of a relative distinguished name
151                 /// (RDN), normalized.
152                 /// 
153                 /// </summary>
154                 /// <param name="rdn">    The relative distinguished name, or in other words,
155                 /// the left-most component of a distinguished name.
156                 /// 
157                 /// </param>
158                 /// <param name="noTypes">  If true, returns only the values of the
159                 /// components, and not the names of the component, for
160                 /// example "Babs Jensen" instead of "cn=Babs Jensen".
161                 /// 
162                 /// </param>
163                 /// <returns> An array of strings representing the individual components
164                 /// of an RDN, or null if the RDN is not a valid RDN.
165                 /// </returns>
166                 public static System.String[] explodeRDN(System.String rdn, bool noTypes)
167                 {
168                         RDN rdnToExplode = new RDN(rdn);
169                         return rdnToExplode.explodeRDN(noTypes);
170                 }
171                 
172                 /// <summary> Returns true if the string conforms to distinguished name syntax.</summary>
173                 /// <param name="dn">   String to evaluate fo distinguished name syntax.
174                 /// </param>
175                 /// <returns>      true if the dn is valid.
176                 /// </returns>
177                 public static bool isValid(System.String dn)
178                 {
179                         try
180                         {
181                                 new DN(dn);
182                         }
183                         catch (System.ArgumentException iae)
184                         {
185                                 return false;
186                         }
187                         return true;
188                 }
189                 
190                 /// <summary> Returns the DN normalized by removal of non-significant space characters
191                 /// as per RFC 2253, section4.
192                 /// 
193                 /// </summary>
194                 /// <returns>      a normalized string
195                 /// </returns>
196                 public static System.String normalize(System.String dn)
197                 {
198                         DN testDN = new DN(dn);
199                         return testDN.ToString();
200                 }
201                 
202                 
203                 /// <summary> Returns the RDN after unescaping the characters requiring escaping.
204                 /// 
205                 /// For example, for the rdn "cn=Acme\, Inc", the unescapeRDN method
206                 /// returns "cn=Acme, Inc".
207                 /// unescapeRDN unescapes the AttributeValue by
208                 /// removing the '\' when the next character fits the following:
209                 /// ',' '+' '"' '\' 'LESSTHAN' 'GREATERTHAN' ';'
210                 /// '#' if it comes at the beginning of the Attribute Name
211                 /// (without the '\').
212                 /// ' ' (space) if it comes at the beginning or the end of the Attribute Name
213                 /// 
214                 /// </summary>
215                 /// <param name="rdn">           The RDN to unescape.
216                 /// 
217                 /// </param>
218                 /// <returns> The RDN with the escaping characters removed.
219                 /// </returns>
220                 public static System.String unescapeRDN(System.String rdn)
221                 {
222                         System.Text.StringBuilder unescaped = new System.Text.StringBuilder();
223                         int i = 0;
224                         
225                         while (i < rdn.Length && rdn[i] != '=')
226                         {
227                                 i++; //advance until we find the separator =
228                         }
229                         if (i == rdn.Length)
230                         {
231                                 throw new System.ArgumentException("Could not parse rdn: Attribute " + "type and name must be separated by an equal symbol, '='");
232                         }
233                         i++;
234                         //check if the first two chars are "\ " (slash space) or "\#"
235                         if ((rdn[i] == '\\') && (i + 1 < rdn.Length - 1) && ((rdn[i + 1] == ' ') || (rdn[i + 1] == '#')))
236                         {
237                                 i++;
238                         }
239                         for (; i < rdn.Length; i++)
240                         {
241                                 //if the current char is a slash, not the end char, and is followed
242                                 // by a special char then...
243                                 if ((rdn[i] == '\\') && (i != rdn.Length - 1))
244                                 {
245                                         if ((rdn[i + 1] == ',') || (rdn[i + 1] == '+') || (rdn[i + 1] == '"') || (rdn[i + 1] == '\\') || (rdn[i + 1] == '<') || (rdn[i + 1] == '>') || (rdn[i + 1] == ';'))
246                                         {
247                                                 //I'm not sure if I have to check for these special chars
248                                                 continue;
249                                         }
250                                         //check if the last two chars are "\ "
251                                         else if ((rdn[i + 1] == ' ') && (i + 2 == rdn.Length))
252                                         {
253                                                 //if the last char is a space
254                                                 continue;
255                                         }
256                                 }
257                                 unescaped.Append(rdn[i]);
258                         }
259                         return unescaped.ToString();
260                 }
261         } //end class LdapDN
262 }