error messages review
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / LdapSearchResults.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.LdapSearchResults.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> An LdapSearchResults object is returned from a synchronous search
39         /// operation. It provides access to all results received during the
40         /// operation (entries and exceptions).
41         /// 
42         /// </summary>
43         /// <seealso cref="LdapConnection.Search">
44         /// </seealso>
45         public class LdapSearchResults
46         {
47                 /// <summary> Returns a count of the items in the search result.
48                 /// 
49                 /// Returns a count of the entries and exceptions remaining in the object.
50                 /// If the search was submitted with a batch size greater than zero,
51                 /// getCount reports the number of results received so far but not enumerated
52                 /// with next().  If batch size equals zero, getCount reports the number of
53                 /// items received, since the application thread blocks until all results are
54                 /// received.
55                 /// 
56                 /// </summary>
57                 /// <returns> The number of items received but not retrieved by the application
58                 /// </returns>
59                 virtual public int Count
60                 {
61                         get
62                         {
63                                 int qCount = queue.MessageAgent.Count;
64                                 return entryCount - entryIndex + referenceCount - referenceIndex + qCount;
65                         }
66                         
67                 }
68                 /// <summary> Returns the latest server controls returned by the server
69                 /// in the context of this search request, or null
70                 /// if no server controls were returned.
71                 /// 
72                 /// </summary>
73                 /// <returns> The server controls returned with the search request, or null
74                 /// if none were returned.
75                 /// </returns>
76                 virtual public LdapControl[] ResponseControls
77                 {
78                         get
79                         {
80                                 return controls;
81                         }
82                         
83                 }
84                 /// <summary> Collects batchSize elements from an LdapSearchQueue message
85                 /// queue and places them in a Vector.
86                 /// 
87                 /// If the last message from the server,
88                 /// the result message, contains an error, it will be stored in the Vector
89                 /// for nextElement to process. (although it does not increment the search
90                 /// result count) All search result entries will be placed in the Vector.
91                 /// If a null is returned from getResponse(), it is likely that the search
92                 /// was abandoned.
93                 /// 
94                 /// </summary>
95                 /// <returns> true if all search results have been placed in the vector.
96                 /// </returns>
97                 private bool BatchOfResults
98                 {
99                         get
100                         {
101                                 LdapMessage msg;
102                                 
103                                 // <=batchSize so that we can pick up the result-done message
104                                 for (int i = 0; i < batchSize; )
105                                 {
106                                         try
107                                         {
108                                                 if ((msg = queue.getResponse()) != null)
109                                                 {
110                                                         // Only save controls if there are some
111                                                         LdapControl[] ctls = msg.Controls;
112                                                         if (ctls != null)
113                                                         {
114                                                                 
115                                                                 controls = ctls;
116                                                         }
117                                                         
118                                                         if (msg is LdapSearchResult)
119                                                         {
120                                                                 // Search Entry
121                                                                 System.Object entry = ((LdapSearchResult) msg).Entry;
122                                                                 entries.Add(entry);
123                                                                 i++;
124                                                                 entryCount++;
125                                                         }
126                                                         else if (msg is LdapSearchResultReference)
127                                                         {
128                                                                 // Search Ref
129                                                                 System.String[] refs = ((LdapSearchResultReference) msg).Referrals;
130                                                                 
131                                                                 if (cons.ReferralFollowing)
132                                                                 {
133 //                                                                      referralConn = conn.chaseReferral(queue, cons, msg, refs, 0, true, referralConn);
134                                                                 }
135                                                                 else
136                                                                 {
137                                                                         references.Add(refs);
138                                                                         referenceCount++;
139                                                                 }
140                                                         }
141                                                         else
142                                                         {
143                                                                 // LdapResponse
144                                                                 LdapResponse resp = (LdapResponse) msg;
145                                                                 int resultCode = resp.ResultCode;
146                                                                 // Check for an embedded exception
147                                                                 if (resp.hasException())
148                                                                 {
149                                                                         // Fake it, results in an exception when msg read
150                                                                         resultCode = LdapException.CONNECT_ERROR;
151                                                                 }
152                                                                 
153                                                                 if ((resultCode == LdapException.REFERRAL) && cons.ReferralFollowing)
154                                                                 {
155                                                                         // Following referrals
156 //                                                                      referralConn = conn.chaseReferral(queue, cons, resp, resp.Referrals, 0, false, referralConn);
157                                                                 }
158                                                                 else if (resultCode != LdapException.SUCCESS)
159                                                                 {
160                                                                         // Results in an exception when message read
161                                                                         entries.Add(resp);
162                                                                         entryCount++;
163                                                                 }
164                                                                 // We are done only when we have read all messages
165                                                                 // including those received from following referrals
166                                                                 int[] msgIDs = queue.MessageIDs;
167                                                                 if (msgIDs.Length == 0)
168                                                                 {
169                                                                         // Release referral exceptions
170 //                                                                      conn.releaseReferralConnections(referralConn);
171                                                                         return true; // search completed
172                                                                 }
173                                                                 else
174                                                                 {
175                                                                 }
176                                                                 continue;
177                                                         }
178                                                 }
179                                                 else
180                                                 {
181                                                         // We get here if the connection timed out
182                                                         // we have no responses, no message IDs and no exceptions
183                                                         LdapException e = new LdapException(null, LdapException.Ldap_TIMEOUT, (System.String) null);
184                                                         entries.Add(e);
185                                                         break;
186                                                 }
187                                         }
188                                         catch (LdapException e)
189                                         {
190                                                 // Hand exception off to user
191                                                 entries.Add(e);
192                                         }
193                                         continue;
194                                 }
195                                 return false; // search not completed
196                         }
197                         
198                 }
199                 
200                 private System.Collections.ArrayList entries; // Search entries
201                 private int entryCount; // # Search entries in vector
202                 private int entryIndex; // Current position in vector
203                 private System.Collections.ArrayList references; // Search Result References
204                 private int referenceCount; // # Search Result Reference in vector
205                 private int referenceIndex; // Current position in vector
206                 private int batchSize; // Application specified batch size
207                 private bool completed = false; // All entries received
208                 private LdapControl[] controls = null; // Last set of controls
209                 private LdapSearchQueue queue;
210                 private static System.Object nameLock; // protect resultsNum
211                 private static int resultsNum = 0; // used for debug
212                 private System.String name; // used for debug
213                 private LdapConnection conn; // LdapConnection which started search
214                 private LdapSearchConstraints cons; // LdapSearchConstraints for search
215                 private System.Collections.ArrayList referralConn = null; // Referral Connections
216                 
217                 /// <summary> Constructs a queue object for search results.
218                 /// 
219                 /// </summary>
220                 /// <param name="conn">The LdapConnection which initiated the search
221                 /// 
222                 /// </param>
223                 /// <param name="queue">The queue for the search results.
224                 /// 
225                 /// </param>
226                 /// <param name="cons">The LdapSearchConstraints associated with this search
227                 /// </param>
228                 /* package */
229                 internal LdapSearchResults(LdapConnection conn, LdapSearchQueue queue, LdapSearchConstraints cons)
230                 {
231                         // setup entry Vector
232                         this.conn = conn;
233                         this.cons = cons;
234                         int batchSize = cons.BatchSize;
235                         int vectorIncr = (batchSize == 0)?64:0;
236                         entries = new System.Collections.ArrayList((batchSize == 0)?64:batchSize);
237                         entryCount = 0;
238                         entryIndex = 0;
239                         
240                         // setup search reference Vector
241                         references = new System.Collections.ArrayList(5);
242                         referenceCount = 0;
243                         referenceIndex = 0;
244                         
245                         this.queue = queue;
246                         this.batchSize = (batchSize == 0)?System.Int32.MaxValue:batchSize;
247                         
248                         return ;
249                 }
250                 
251                 /// <summary> Reports if there are more search results.
252                 /// 
253                 /// </summary>
254                 /// <returns> true if there are more search results.
255                 /// </returns>
256                 public virtual bool hasMore()
257                 {
258                         bool ret = false;
259                         if ((entryIndex < entryCount) || (referenceIndex < referenceCount))
260                         {
261                                 // we have data
262                                 ret = true;
263                         }
264                         else if (completed == false)
265                         {
266                                 // reload the Vector by getting more results
267                                 resetVectors();
268                                 ret = (entryIndex < entryCount) || (referenceIndex < referenceCount);
269                         }
270                         return ret;
271                 }
272                 
273                 /*
274                 * If both of the vectors are empty, get more data for them.
275                 */
276                 private void  resetVectors()
277                 {
278                         // If we're done, no further checking needed
279                         if (completed)
280                         {
281                                 return ;
282                         }
283                         // Checks if we have run out of references
284                         if ((referenceIndex != 0) && (referenceIndex >= referenceCount))
285                         {
286                                 SupportClass.SetSize(references, 0);
287                                 referenceCount = 0;
288                                 referenceIndex = 0;
289                         }
290                         // Checks if we have run out of entries
291                         if ((entryIndex != 0) && (entryIndex >= entryCount))
292                         {
293                                 SupportClass.SetSize(entries, 0);
294                                 entryCount = 0;
295                                 entryIndex = 0;
296                         }
297                         // If no data at all, must reload enumeration
298                         if ((referenceIndex == 0) && (referenceCount == 0) && (entryIndex == 0) && (entryCount == 0))
299                         {
300                                 completed = BatchOfResults;
301                         }
302                         return ;
303                 }
304                 /// <summary> Returns the next result as an LdapEntry.
305                 /// 
306                 /// If automatic referral following is disabled or if a referral
307                 /// was not followed, next() will throw an LdapReferralException
308                 /// when the referral is received.
309                 /// 
310                 /// </summary>
311                 /// <returns> The next search result as an LdapEntry.
312                 /// 
313                 /// </returns>
314                 /// <exception> LdapException A general exception which includes an error
315                 /// message and an Ldap error code.
316                 /// </exception>
317                 /// <exception> LdapReferralException A referral was received and not
318                 /// followed.
319                 /// </exception>
320                 public virtual LdapEntry next()
321                 {
322                         if (completed && (entryIndex >= entryCount) && (referenceIndex >= referenceCount))
323                         {
324                                 throw new System.ArgumentOutOfRangeException("LdapSearchResults.next() no more results");
325                         }
326                         // Check if the enumeration is empty and must be reloaded
327                         resetVectors();
328                         
329                         System.Object element = null;
330                         // Check for Search References & deliver to app as they come in
331                         // We only get here if not following referrals/references
332                         if (referenceIndex < referenceCount)
333                         {
334                                 System.String[] refs = (System.String[]) (references[referenceIndex++]);
335                                 LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_NOFOLLOW);
336                                 rex.setReferrals(refs);
337                                 throw rex;
338                         }
339                         else if (entryIndex < entryCount)
340                         {
341                                 // Check for Search Entries and the Search Result
342                                 element = entries[entryIndex++];
343                                 if (element is LdapResponse)
344                                 {
345                                         // Search done w/bad status
346                                         if (((LdapResponse) element).hasException())
347                                         {
348                                                 
349                                                 LdapResponse lr = (LdapResponse) element;
350                                                 ReferralInfo ri = lr.ActiveReferral;
351                                                 
352                                                 if (ri != null)
353                                                 {
354                                                         // Error attempting to follow a search continuation reference
355                                                         LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERENCE_ERROR, lr.Exception);
356                                                         rex.setReferrals(ri.ReferralList);
357                                                         rex.FailedReferral = ri.ReferralUrl.ToString();
358                                                         throw rex;
359                                                 }
360                                         }
361                                         // Throw an exception if not success
362                                         ((LdapResponse) element).chkResultCode();
363                                 }
364                                 else if (element is LdapException)
365                                 {
366                                         throw (LdapException) element;
367                                 }
368                         }
369                         else
370                         {
371                                 // If not a Search Entry, Search Result, or search continuation
372                                 // we are very confused.
373                                 // LdapSearchResults.next(): No entry found & request is not complete
374                                 throw new LdapException(ExceptionMessages.REFERRAL_LOCAL, new System.Object[]{"next"}, LdapException.LOCAL_ERROR, (System.String) null);
375                         }
376                         return (LdapEntry) element;
377                 }
378                 
379                 /// <summary> Cancels the search request and clears the message and enumeration.</summary>
380                 /*package*/
381                 internal virtual void  Abandon()
382                 {
383                         // first, remove message ID and timer and any responses in the queue
384                         queue.MessageAgent.AbandonAll();
385                         
386                         // next, clear out enumeration
387                         resetVectors();
388                         completed = true;
389                 }
390                 static LdapSearchResults()
391                 {
392                         nameLock = new System.Object();
393                 }
394         }
395 }