private bool IDLE(string cmdTag,string cmdText)
{
/* RFC 2177 3. IDLE Command.
Arguments: none
Responses: continuation data will be requested; the client sends
the continuation data "DONE" to end the command
Result: OK - IDLE completed after client sent "DONE"
NO - failure: the server will not allow the IDLE
command at this time
BAD - command unknown or arguments invalid
The IDLE command may be used with any IMAP4 server implementation
that returns "IDLE" as one of the supported capabilities to the
CAPABILITY command. If the server does not advertise the IDLE
capability, the client MUST NOT use the IDLE command and must poll
for mailbox updates. In particular, the client MUST continue to be
able to accept unsolicited untagged responses to ANY command, as
specified in the base IMAP specification.
The IDLE command is sent from the client to the server when the
client is ready to accept unsolicited mailbox update messages. The
server requests a response to the IDLE command using the continuation
("+") response. The IDLE command remains active until the client
responds to the continuation, and as long as an IDLE command is
active, the server is now free to send untagged EXISTS, EXPUNGE, and
other messages at any time.
The IDLE command is terminated by the receipt of a "DONE"
continuation from the client; such response satisfies the server's
continuation request. At that point, the server MAY send any
remaining queued untagged responses and then MUST immediately send
the tagged response to the IDLE command and prepare to process other
commands. As in the base specification, the processing of any new
command may cause the sending of unsolicited untagged responses,
subject to the ambiguity limitations. The client MUST NOT send a
command while the server is waiting for the DONE, since the server
will not be able to distinguish a command from a continuation.
The server MAY consider a client inactive if it has an IDLE command
running, and if such a server has an inactivity timeout it MAY log
the client off implicitly at the end of its timeout period. Because
of that, clients using IDLE are advised to terminate the IDLE and
re-issue it at least every 29 minutes to avoid being logged off.
This still allows a client to receive immediate mailbox updates even
though it need only "poll" at half hour intervals.
Example: C: A001 SELECT INBOX
S: * FLAGS (Deleted Seen)
S: * 3 EXISTS
S: * 0 RECENT
S: * OK [UIDVALIDITY 1]
S: A001 OK SELECT completed
C: A002 IDLE
S: + idling
...time passes; new mail arrives...
S: * 4 EXISTS
C: DONE
S: A002 OK IDLE terminated
...another client expunges message 2 now...
C: A003 FETCH 4 ALL
S: * 4 FETCH (...)
S: A003 OK FETCH completed
C: A004 IDLE
S: * 2 EXPUNGE
S: * 3 EXISTS
S: + idling
...time passes; another client expunges message 3...
S: * 3 EXPUNGE
S: * 2 EXISTS
...time passes; new mail arrives...
S: * 3 EXISTS
C: DONE
S: A004 OK IDLE terminated
C: A005 FETCH 3 ALL
S: * 3 FETCH (...)
S: A005 OK FETCH completed
*/
if(!this.IsAuthenticated){
m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required."));
return true;
}
m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus("+","idling"));
TimerEx timer = new TimerEx(30000,true);
timer.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object sender,System.Timers.ElapsedEventArgs e){
try{
UpdateSelectedFolderAndSendChanges();
}
catch{
}
});
timer.Enabled = true;
// Read client response.
SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[32000],SizeExceededAction.JunkAndThrowException);
//.........这里部分代码省略.........
/// <summary>
/// Starts transaction processing.
/// </summary>
/// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
/// <exception cref="InvalidOperationException">Is raised when <b>Start</b> is called other state than 'WaitingToStart'.</exception>
public void Start()
{
lock(this.SyncRoot){
if(this.State == SIP_TransactionState.Disposed){
throw new ObjectDisposedException(this.GetType().Name);
}
else if(this.State != SIP_TransactionState.WaitingToStart){
throw new InvalidOperationException("Start method is valid only in 'WaitingToStart' state.");
}
// Move processing to thread pool.
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state){
lock(this.SyncRoot){
#region INVITE
if(this.Method == SIP_Methods.INVITE){
/* RFC 3261 17.1.1.2.
The initial state, "calling", MUST be entered when the TU
initiates a new client transaction with an INVITE request. The
client transaction MUST pass the request to the transport layer for
transmission (see Section 18). If an unreliable transport is being
used, the client transaction MUST start timer A with a value of T1.
If a reliable transport is being used, the client transaction SHOULD
NOT start timer A (Timer A controls request retransmissions). For
any transport, the client transaction MUST start timer B with a value
of 64*T1 seconds (Timer B controls transaction timeouts).
*/
SetState(SIP_TransactionState.Calling);
try{
// Send initial request.
this.Stack.TransportLayer.SendRequest(this.Flow,this.Request,this);
}
catch(Exception x){
OnTransportError(x);
// NOTE: TransportError event handler could Dispose this transaction, so we need to check it.
if(this.State != SIP_TransactionState.Disposed){
SetState(SIP_TransactionState.Terminated);
}
return;
}
// Start timer A for unreliable transports.
if(!this.Flow.IsReliable){
m_pTimerA = new TimerEx(SIP_TimerConstants.T1,false);
m_pTimerA.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerA_Elapsed);
m_pTimerA.Enabled = true;
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer A(INVITE request retransmission) started, will trigger after " + m_pTimerA.Interval + ".");
}
}
// Start timer B.
m_pTimerB = new TimerEx(64 * SIP_TimerConstants.T1,false);
m_pTimerB.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerB_Elapsed);
m_pTimerB.Enabled = true;
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer B(INVITE calling state timeout) started, will trigger after " + m_pTimerB.Interval + ".");
}
}
#endregion
#region Non-INVITE
else{
/* RFC 3261 17.1.2.2.
The "Trying" state is entered when the TU initiates a new client
transaction with a request. When entering this state, the client
transaction SHOULD set timer F to fire in 64*T1 seconds. The request
MUST be passed to the transport layer for transmission. If an
unreliable transport is in use, the client transaction MUST set timer
E to fire in T1 seconds.
*/
SetState(SIP_TransactionState.Trying);
// Start timer F.
m_pTimerF = new TimerEx(64 * SIP_TimerConstants.T1,false);
m_pTimerF.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerF_Elapsed);
m_pTimerF.Enabled = true;
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) started, will trigger after " + m_pTimerF.Interval + ".");
}
try{
// Send initial request.
this.Stack.TransportLayer.SendRequest(this.Flow,this.Request,this);
//.........这里部分代码省略.........
/// <summary>
/// Starts transaction processing.
/// </summary>
private void Start()
{
#region INVITE
if(this.Method == SIP_Methods.INVITE){
/* RFC 3261 17.2.1.
When a server transaction is constructed for a request, it enters the "Proceeding" state. The server
transaction MUST generate a 100 (Trying) response unless it knows that the TU will generate a provisional
or final response within 200 ms, in which case it MAY generate a 100 (Trying) response.
*/
SetState(SIP_TransactionState.Proceeding);
m_pTimer100 = new TimerEx(200,false);
m_pTimer100.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimer100_Elapsed);
m_pTimer100.Enabled = true;
}
#endregion
#region Non-INVITE
else{
// RFC 3261 17.2.2. The state machine is initialized in the "Trying" state.
SetState(SIP_TransactionState.Trying);
}
#endregion
}
//.........这里部分代码省略.........
#endregion
#region ACK
else if(request.RequestLine.Method == SIP_Methods.ACK){
#region Accepeted
if(this.State == SIP_TransactionState.Accpeted){
}
#endregion
#region Completed
else if(this.State == SIP_TransactionState.Completed){
/* RFC 3261 17.2.1
If an ACK is received while the server transaction is in the "Completed" state, the server transaction
MUST transition to the "Confirmed" state. As Timer G is ignored in this state, any retransmissions of the
response will cease.
When this state is entered, timer I is set to fire in T4 seconds for unreliable transports,
and zero seconds for reliable transports.
*/
SetState(SIP_TransactionState.Confirmed);
// Stop timers G,H
if(m_pTimerG != null){
m_pTimerG.Dispose();
m_pTimerG = null;
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stopped.");
}
}
if(m_pTimerH != null){
m_pTimerH.Dispose();
m_pTimerH = null;
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer H(INVITE ACK wait) stopped.");
}
}
// Start timer I.
m_pTimerI = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4),false);
m_pTimerI.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerI_Elapsed);
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer I(INVITE ACK retransission wait) started, will trigger after " + m_pTimerI.Interval + ".");
}
m_pTimerI.Enabled = true;
}
#endregion
}
#endregion
}
#endregion
#region Non-INVITE
else{
// Non-INVITE transaction may have only request retransmission requests.
if(this.Method == request.RequestLine.Method){
if(this.State == SIP_TransactionState.Proceeding){
/* RFC 3261 17.2.2.
If a retransmission of the request is received while in the "Proceeding" state, the most
recently sent provisional response MUST be passed to the transport layer for retransmission.
*/
this.Stack.TransportLayer.SendResponse(this,this.LastProvisionalResponse);
}
else if(this.State == SIP_TransactionState.Completed){
/* RFC 3261 17.2.2.
While in the "Completed" state, the server transaction MUST pass the final response to the transport
layer for retransmission whenever a retransmission of the request is received.
*/
this.Stack.TransportLayer.SendResponse(this,this.FinalResponse);
}
}
}
#endregion
}
catch(SIP_TransportException x){
// Log
if(this.Stack.Logger != null){
this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] transport exception: " + x.Message);
}
OnTransportError(x);
}
}
}
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="dialog">Owner INVITE dialog.</param>
/// <param name="response">INVITE 2xx response.</param>
/// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>response</b> is null reference.</exception>
public UasInvite2xxRetransmit(SIP_Dialog_Invite dialog, SIP_Response response)
{
if (dialog == null)
{
throw new ArgumentNullException("dialog");
}
if (response == null)
{
throw new ArgumentNullException("response");
}
m_pDialog = dialog;
m_pResponse = response;
/* RFC 3261 13.3.1.4.
Once the response has been constructed, it is passed to the INVITE
server transaction. Note, however, that the INVITE server
transaction will be destroyed as soon as it receives this final
response and passes it to the transport. Therefore, it is necessary
to periodically pass the response directly to the transport until the
ACK arrives. The 2xx response is passed to the transport with an
interval that starts at T1 seconds and doubles for each
retransmission until it reaches T2 seconds (T1 and T2 are defined in
Section 17). Response retransmissions cease when an ACK request for
the response is received. This is independent of whatever transport
protocols are used to send the response.
Since 2xx is retransmitted end-to-end, there may be hops between
UAS and UAC that are UDP. To ensure reliable delivery across
these hops, the response is retransmitted periodically even if the
transport at the UAS is reliable.
*/
m_pTimer = new TimerEx(SIP_TimerConstants.T1, false);
m_pTimer.Elapsed += m_pTimer_Elapsed;
m_pTimer.Enabled = true;
}
请发表评论