context: /******************************************************************************
  * The MIT License
  * Copyright (c) 2003 Novell Inc.  www.novell.com
  * 
  * Permission is hereby granted, free of charge, to any person obtaining  a copy
  * of this software and associated documentation files (the Software), to deal
  * in the Software without restriction, including  without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  * copies of the Software, and to  permit persons to whom the Software is 
  * furnished to do so, subject to the following conditions:
  * 
  * The above copyright notice and this permission notice shall be included in 
  * all copies or substantial portions of the Software.
  * 
  * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *******************************************************************************/
  //
  // Novell.Directory.Ldap.Message.cs
  //
  // Author:
  //   Sunil Kumar (Sunilk@novell.com)
  //
  // (C) 2003 Novell, Inc (http://www.novell.com)
  //
  
  using System;
  using Novell.Directory.Ldap.Rfc2251;
  using Novell.Directory.Ldap.Utilclass;
  
  namespace Novell.Directory.Ldap
  {
  	
  	/// <summary> Encapsulates an Ldap message, its state, and its replies.</summary>
  	/* package */
  	class Message
  	{
  		private void  InitBlock()
  		{
  			replies = new MessageVector(5, 5);
  		}
  		/// <summary> Get number of messages queued.
  		/// Don't count the last message containing result code.
  		/// </summary>
  		virtual internal int Count
  		{
  			/* package */
  			
  			get
  			{
  				int size = replies.Count;
  				if (complete)
  				{
  					return (size > 0?(size - 1):size);
  				}
  				else
  				{
  					return size;
  				}
  			}
  			
  		}
  
  		/// <summary> sets the agent for this message</summary>
  		virtual internal MessageAgent Agent
  		{
  			/* package */
  			
  			set
  			{
  				this.agent = value;
  				return ;
  			}
  			
  		}
  
  		/// <summary> Returns true if replies are queued
  		/// 
  		/// </summary>
  		/// <returns> false if no replies are queued, otherwise true
  		/// </returns>
  		/* package */
  		internal virtual bool hasReplies()
  		{
  			if (replies == null)
  			{
  				// abandoned request
  				return false;
  			}
  			return (replies.Count > 0);
  		}
  
  		virtual internal int MessageType
  		{
  			/* package */
  			
  			get
  			{
  				if (msg == null)
  				{
  					return - 1;
  				}
  				return msg.Type;
  			}
  			
  		}
  
  		virtual internal int MessageID
  		{
  			/* package */
  			
  			get
  			{
  				return msgId;
  			}
  			
  		}
  
  		/// <summary> gets the operation complete status for this message
  		/// 
  		/// </summary>
  		/// <returns> the true if the operation is complete, i.e.
  		/// the LdapResult has been received.
  		/// </returns>
  		virtual internal bool Complete
  		{
  			/* package */
  			
  			get
  			{
  				return complete;
  			}
  			
  		}
  
  		/// <summary> Gets the next reply from the reply queue or waits until one is there
  		/// 
  		/// </summary>
  		/// <returns> the next reply message on the reply queue or null
  		/// </returns>
  		/* package */
  		internal virtual System.Object waitForReply()
  		{
  			if (replies == null)
  			{
  				return null;
  			}
  			// sync on message so don't confuse with timer thread
  			lock (replies.SyncRoot)
  			{
  				System.Object msg = null;
  				while (waitForReply_Renamed_Field)
  				{
  					if ((replies.Count == 0))
  					{
  						try
  						{
  							System.Threading.Monitor.Wait(replies.SyncRoot);
  						}
  						catch (System.Threading.ThreadInterruptedException ir)
  						{
  							; // do nothing
  						}
  						if (waitForReply_Renamed_Field)
  						{
  							continue;
  						}
  						else
  						{
  							break;
  						}
  					}
  					else
  					{
  						System.Object temp_object;
  						temp_object = replies[0];
  						replies.RemoveAt(0);
  						msg = temp_object; // Atomic get and remove
  					}
  					if ((complete || !acceptReplies) && (replies.Count == 0))
  					{
  						// Remove msg from connection queue when last reply read
  						conn.removeMessage(this);
  					}
  					else
  					{
  					}
  					return msg;
  				}
  				return null;
  			}
  		}
  		
  
  		/// <summary> Gets the next reply from the reply queue if one exists
  		/// 
  		/// </summary>
  		/// <returns> the next reply message on the reply queue or null if none
  		/// </returns>
  		virtual internal System.Object Reply
  		{
  			/* package */
  			
  			get
  			{
  				System.Object msg;
  				if (replies == null)
  				{
  					return null;
  				}
  				lock (replies.SyncRoot)
  				{
  					// Test and remove must be atomic
  					if ((replies.Count == 0))
  					{
  						return null; // No data
  					}
  					System.Object temp_object;
  					temp_object = replies[0];
  					replies.RemoveAt(0);
  					msg = temp_object; // Atomic get and remove
  				}
  				if ((conn != null) && (complete || !acceptReplies) && (replies.Count == 0))
  				{
  					// Remove msg from connection queue when last reply read
  					conn.removeMessage(this);
  				}
  				return msg;
  			}
  			
  		}
  
  		/// <summary> Returns true if replies are accepted for this request.
  		/// 
  		/// </summary>
  		/// <returns> false if replies are no longer accepted for this request
  		/// </returns>
  		/* package */
  		internal virtual bool acceptsReplies()
  		{
  			return acceptReplies;
  		}
  
  		/// <summary> gets the LdapMessage request associated with this message
  		/// 
  		/// </summary>
  		/// <returns> the LdapMessage request associated with this message
  		/// </returns>
  		virtual internal LdapMessage Request
  		{
  			/*package*/
  			
  			get
  			{
  				return msg;
  			}
  			
  		}
  
  
  		virtual internal bool BindRequest
  		{
  			/* package */
  			
  			get
  			{
  				return (bindprops != null);
  			}
  			
  		}
  
  
  		/// <summary> gets the MessageAgent associated with this message
  		/// 
  		/// </summary>
  		/// <returns> the MessageAgent associated with this message
  		/// </returns>
  		virtual internal MessageAgent MessageAgent
  		{
  			/* package */
  			
  			get
  			{
  				return agent;
  			}
  			
  		}
  
  		private LdapMessage msg; // msg request sent to server
  		private Connection conn; // Connection object where msg sent
  		private MessageAgent agent; // MessageAgent handling this request
  		private LdapMessageQueue queue; // Application message queue
  		private int mslimit; // client time limit in milliseconds
  		private SupportClass.ThreadClass timer = null; // Timeout thread
  		// Note: MessageVector is synchronized
  		private MessageVector replies; // place to store replies
  		private int msgId; // message ID of this request
  		private bool acceptReplies = true; // false if no longer accepting replies
  		private bool waitForReply_Renamed_Field = true; // true if wait for reply
  		private bool complete = false; // true LdapResult received
  		private System.String name; // String name used for Debug
  		private BindProperties bindprops; // Bind properties if a bind request
  		
  		internal Message(LdapMessage msg, int mslimit, Connection conn, MessageAgent agent, LdapMessageQueue queue, BindProperties bindprops)
  		{
  			InitBlock();
  			this.msg = msg;
  			this.conn = conn;
  			this.agent = agent;
  			this.queue = queue;
  			this.mslimit = mslimit;
  			this.msgId = msg.MessageID;
  			this.bindprops = bindprops;
  			return ;
  		}
  
  		internal void  sendMessage()
  		{
  			conn.writeMessage(this);
  			// Start the timer thread
  			if (mslimit != 0)
  			{
  				// Don't start the timer thread for abandon or Unbind
  				switch (msg.Type)
  				{
  					
  					case LdapMessage.ABANDON_REQUEST: 
  					case LdapMessage.UNBIND_REQUEST: 
  						mslimit = 0;
  						break;
  					
  					default: 
  						timer = new Timeout(this, mslimit, this);
  						timer.IsBackground = true; // If this is the last thread running, allow exit.
  						timer.Start();
  						break;
  					
  				}
  			}
  			return ;
  		}
  
  		internal virtual void  Abandon(LdapConstraints cons, InterThreadException informUserEx)
  		{
  			if (!waitForReply_Renamed_Field)
  			{
  				return ;
  			}
  			acceptReplies = false; // don't listen to anyone
  			waitForReply_Renamed_Field = false; // don't let sleeping threads lie
  			if (!complete)
  			{
  				try
  				{
  					// If a bind, release bind semaphore & wake up waiting threads
  					// Must do before writing abandon message, otherwise deadlock
  					if (bindprops != null)
  					{
  						int id;
  						if (conn.BindSemIdClear)
  						{
  							// Semaphore id for normal operations
  							id = msgId;
  						}
  						else
  						{
  							// Semaphore id for sasl bind
  							id = conn.BindSemId;
  							conn.clearBindSemId();
  						}
  						conn.freeWriteSemaphore(id);
  					}
  					
  					// Create the abandon message, but don't track it.
  					LdapControl[] cont = null;
  					if (cons != null)
  					{
  						cont = cons.getControls();
  					}
  
gt: 					LdapMessage msg = new LdapAbandonRequest(msgId, cont);