1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 * Copyright (c) 2003-2012 by AG-Software * 3 * All Rights Reserved. * 4 * Contact information for AG-Software is available at http://www.ag-software.de * 5 * * 6 * Licence: * 7 * The agsXMPP SDK is released under a dual licence * 8 * agsXMPP can be used under either of two licences * 9 * * 10 * A commercial licence which is probably the most appropriate for commercial * 11 * corporate use and closed source projects. * 12 * * 13 * The GNU Public License (GPL) is probably most appropriate for inclusion in * 14 * other open source projects. * 15 * * 16 * See README.html for details. * 17 * * 18 * For general enquiries visit our website at: * 19 * http://www.ag-software.de * 20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 21 22 using System; 23 using System.Text; 24 using System.Threading; 25 26 using agsXMPP.Net; 27 using agsXMPP.protocol.extensions.bosh; 28 29 using agsXMPP.Xml; 30 using agsXMPP.Xml.Dom; 31 using agsXMPP.Idn; 32 33 namespace agsXMPP 34 { XmlHandler(object sender, string xml)35 public delegate void XmlHandler (object sender, string xml); ErrorHandler(object sender, Exception ex)36 public delegate void ErrorHandler (object sender, Exception ex); 37 XmppConnectionStateHandler(object sender, XmppConnectionState state)38 public delegate void XmppConnectionStateHandler (object sender, XmppConnectionState state); 39 /// <summary> 40 /// abstract base class XmppConnection. 41 /// </summary> 42 public abstract class XmppConnection 43 { 44 45 private Timer m_KeepaliveTimer = null; 46 47 #region << Events >> 48 /// <summary> 49 /// This event just informs about the current state of the XmppConnection 50 /// </summary> 51 public event XmppConnectionStateHandler OnXmppConnectionStateChanged; 52 53 /// <summary> 54 /// a XML packet or text is received. 55 /// This are no winsock events. The Events get generated from the XML parser 56 /// </summary> 57 public event XmlHandler OnReadXml; 58 /// <summary> 59 /// XML or Text is written to the Socket this includes also the keep alive packages (a single space) 60 /// </summary> 61 public event XmlHandler OnWriteXml; 62 63 public event ErrorHandler OnError; 64 65 /// <summary> 66 /// Data received from the Socket 67 /// </summary> 68 public event BaseSocket.OnSocketDataHandler OnReadSocketData; 69 70 /// <summary> 71 /// Data was sent to the socket for sending 72 /// </summary> 73 public event BaseSocket.OnSocketDataHandler OnWriteSocketData; 74 75 #endregion 76 77 #region << Constructors >> XmppConnection()78 public XmppConnection() 79 { 80 InitSocket(); 81 // Streamparser stuff 82 m_StreamParser = new StreamParser(); 83 84 m_StreamParser.OnStreamStart += new StreamHandler(StreamParserOnStreamStart); 85 m_StreamParser.OnStreamEnd += new StreamHandler(StreamParserOnStreamEnd); 86 m_StreamParser.OnStreamElement += StreamParserOnStreamElement; 87 m_StreamParser.StreamElementNotHandled += StreamParserStreamElementNotHandled; 88 m_StreamParser.OnStreamError += new StreamError (StreamParserOnStreamError); 89 m_StreamParser.OnError += new ErrorHandler (StreamParserOnError); 90 } 91 XmppConnection(agsXMPP.Net.SocketConnectionType type)92 public XmppConnection(agsXMPP.Net.SocketConnectionType type) : this() 93 { 94 m_SocketConnectionType = agsXMPP.Net.SocketConnectionType.Direct; 95 } 96 #endregion 97 98 #region << Properties and Member Variables >> 99 private int m_Port = 5222; 100 private string m_Server = null; 101 private string m_ConnectServer = null; 102 private string m_StreamId = ""; 103 private string m_StreamVersion = "1.0"; 104 private XmppConnectionState m_ConnectionState = XmppConnectionState.Disconnected; 105 private BaseSocket m_ClientSocket = null; 106 private StreamParser m_StreamParser = null; 107 private SocketConnectionType m_SocketConnectionType = SocketConnectionType.Direct; 108 private bool m_AutoResolveConnectServer = true; 109 private int m_KeepAliveInterval = 120; 110 private bool m_KeepAlive = true; 111 /// <summary> 112 /// The Port of the remote server for the connection 113 /// </summary> 114 public int Port 115 { 116 get { return m_Port; } 117 set { m_Port = value; } 118 } 119 120 /// <summary> 121 /// domain or ip-address of the remote server for the connection 122 /// </summary> 123 public string Server 124 { 125 get { return m_Server; } 126 set 127 { 128 #if !STRINGPREP 129 if (value != null) 130 m_Server = value.ToLower(); 131 else 132 m_Server = null; 133 #else 134 if (value != null) 135 m_Server = Stringprep.NamePrep(value); 136 else 137 m_Server = null; 138 #endif 139 } 140 } 141 142 /// <summary> 143 /// 144 /// </summary> 145 public string ConnectServer 146 { 147 get { return m_ConnectServer; } 148 set { m_ConnectServer = value; } 149 } 150 151 /// <summary> 152 /// the id of the current xmpp xml-stream 153 /// </summary> 154 public string StreamId 155 { 156 get { return m_StreamId; } 157 set { m_StreamId = value; } 158 } 159 160 /// <summary> 161 /// Set to null for old Jabber Protocol without SASL andstream features 162 /// </summary> 163 public string StreamVersion 164 { 165 get { return m_StreamVersion; } 166 set { m_StreamVersion = value; } 167 } 168 169 public XmppConnectionState XmppConnectionState 170 { 171 get { return m_ConnectionState; } 172 } 173 174 /// <summary> 175 /// Read Online Property ClientSocket 176 /// returns the SOcket object used for this connection 177 /// </summary> 178 public BaseSocket ClientSocket 179 { 180 get { return m_ClientSocket; } 181 } 182 183 /// <summary> 184 /// the underlaying XMPP StreamParser. Normally you don't need it, but we make it accessible for 185 /// low level access to the stream 186 /// </summary> 187 public StreamParser StreamParser 188 { 189 get { return m_StreamParser; } 190 } 191 192 public SocketConnectionType SocketConnectionType 193 { 194 get { return m_SocketConnectionType; } 195 set 196 { 197 m_SocketConnectionType = value; 198 InitSocket(); 199 } 200 } 201 202 public bool AutoResolveConnectServer 203 { 204 get { return m_AutoResolveConnectServer; } 205 set { m_AutoResolveConnectServer = value; } 206 } 207 208 /// <summary> 209 /// <para> 210 /// the keep alive interval in seconds. 211 /// Default value is 120 212 /// </para> 213 /// <para> 214 /// Keep alive packets prevent disconenct on NAT and broadband connections which often 215 /// disconnect if they are idle. 216 /// </para> 217 /// </summary> 218 public int KeepAliveInterval 219 { 220 get 221 { 222 return m_KeepAliveInterval; 223 } 224 set 225 { 226 m_KeepAliveInterval = value; 227 } 228 } 229 /// <summary> 230 /// Send Keep Alives (for NAT) 231 /// </summary> 232 public bool KeepAlive 233 { 234 get 235 { 236 return m_KeepAlive; 237 } 238 set 239 { 240 m_KeepAlive = value; 241 } 242 } 243 #endregion 244 245 #region << Socket handers >> SocketOnConnect(object sender)246 public virtual void SocketOnConnect(object sender) 247 { 248 DoChangeXmppConnectionState(XmppConnectionState.Connected); 249 } 250 SocketOnDisconnect(object sender)251 public virtual void SocketOnDisconnect(object sender) 252 { 253 254 } 255 SocketOnReceive(object sender, byte[] data, int count)256 public virtual void SocketOnReceive(object sender, byte[] data, int count) 257 { 258 259 if (OnReadSocketData != null) 260 OnReadSocketData(sender, data, count); 261 262 // put the received bytes to the parser 263 lock (this) 264 { 265 StreamParser.Push(data, 0, count); 266 } 267 } 268 SocketOnError(object sender, Exception ex)269 public virtual void SocketOnError(object sender, Exception ex) 270 { 271 272 } 273 #endregion 274 275 #region << StreamParser Events >> StreamParserOnStreamStart(object sender, Node e)276 public virtual void StreamParserOnStreamStart (object sender, Node e) 277 { 278 string xml = e.ToString().Trim(); 279 xml = xml.Substring(0, xml.Length - 2) + ">"; 280 281 this.FireOnReadXml(this, xml); 282 283 protocol.Stream st = (protocol.Stream)e; 284 if (st != null) 285 { 286 m_StreamId = st.StreamId; 287 m_StreamVersion = st.Version; 288 } 289 } 290 StreamParserOnStreamEnd(object sender, Node e)291 public virtual void StreamParserOnStreamEnd (object sender, Node e) 292 { 293 Element tag = e as Element; 294 295 string qName; 296 if (tag.Prefix == null) 297 qName = tag.TagName; 298 else 299 qName = tag.Prefix + ":" + tag.TagName; 300 301 string xml = "</" + qName + ">"; 302 303 this.FireOnReadXml(this, xml); 304 } 305 StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs)306 public virtual void StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs) 307 { 308 } 309 StreamParserOnStreamElement(object sender, ElementEventArgs e)310 public virtual void StreamParserOnStreamElement(object sender, ElementEventArgs e) 311 { 312 this.FireOnReadXml(this, e.Element.ToString()); 313 } StreamParserOnStreamError(object sender, Exception ex)314 public virtual void StreamParserOnStreamError (object sender, Exception ex) 315 { 316 } StreamParserOnError(object sender, Exception ex)317 public virtual void StreamParserOnError (object sender, Exception ex) 318 { 319 FireOnError(sender, ex); 320 } 321 #endregion 322 DoChangeXmppConnectionState(XmppConnectionState state)323 internal void DoChangeXmppConnectionState(XmppConnectionState state) 324 { 325 m_ConnectionState = state; 326 327 if (OnXmppConnectionStateChanged!=null) 328 OnXmppConnectionStateChanged(this, state); 329 } 330 InitSocket()331 private void InitSocket() 332 { 333 if (m_ClientSocket != null) { 334 m_ClientSocket.OnConnect -= SocketOnConnect; 335 m_ClientSocket.OnDisconnect -= SocketOnDisconnect; 336 m_ClientSocket.OnReceive -= SocketOnReceive; 337 m_ClientSocket.OnError -= SocketOnError; 338 } 339 m_ClientSocket = null; 340 341 // Socket Stuff 342 if (m_SocketConnectionType == agsXMPP.Net.SocketConnectionType.HttpPolling) 343 m_ClientSocket= new PollClientSocket(); 344 else if (m_SocketConnectionType == agsXMPP.Net.SocketConnectionType.Bosh) 345 m_ClientSocket = new BoshClientSocket(this); 346 else 347 m_ClientSocket = new ClientSocket(); 348 349 m_ClientSocket.OnConnect += SocketOnConnect; 350 m_ClientSocket.OnDisconnect += SocketOnDisconnect; 351 m_ClientSocket.OnReceive += SocketOnReceive; 352 m_ClientSocket.OnError += SocketOnError; 353 } 354 355 /// <summary> 356 /// Starts connecting of the socket 357 /// </summary> SocketConnect()358 public virtual void SocketConnect() 359 { 360 DoChangeXmppConnectionState(XmppConnectionState.Connecting); 361 ClientSocket.Connect(); 362 } 363 SocketConnect(string server, int port)364 public void SocketConnect(string server, int port) 365 { 366 ClientSocket.Address = server; 367 ClientSocket.Port = port; 368 SocketConnect(); 369 } 370 SocketDisconnect()371 public void SocketDisconnect() 372 { 373 m_ClientSocket.Disconnect(); 374 } 375 376 /// <summary> 377 /// Send a xml string over the XmppConnection 378 /// </summary> 379 /// <param name="xml"></param> Send(string xml)380 public void Send(string xml) 381 { 382 FireOnWriteXml(this, xml); 383 m_ClientSocket.Send(xml); 384 385 if (OnWriteSocketData != null) 386 OnWriteSocketData(this, Encoding.UTF8.GetBytes(xml), xml.Length); 387 388 // reset keep alive timer if active to make sure the interval is always idle time from the last 389 // outgoing packet 390 if (m_KeepAlive && m_KeepaliveTimer != null) 391 m_KeepaliveTimer.Change(m_KeepAliveInterval * 1000, m_KeepAliveInterval * 1000); 392 } 393 394 /// <summary> 395 /// Send a xml element over the XmppConnection 396 /// </summary> 397 /// <param name="e"></param> Send(Element e)398 public virtual void Send(Element e) 399 { 400 Send(e.ToString()); 401 } 402 Open(string xml)403 public void Open(string xml) 404 { 405 Send(xml); 406 } 407 408 /// <summary> 409 /// Send the end of stream 410 /// </summary> Close()411 public virtual void Close() 412 { 413 Send("</stream:stream>"); 414 } 415 FireOnReadXml(object sender, string xml)416 protected void FireOnReadXml(object sender, string xml) 417 { 418 if (OnReadXml != null) 419 OnReadXml(sender, xml); 420 } 421 FireOnWriteXml(object sender, string xml)422 protected void FireOnWriteXml(object sender, string xml) 423 { 424 if (OnWriteXml != null) 425 OnWriteXml(sender, xml); 426 } 427 FireOnError(object sender, Exception ex)428 protected void FireOnError(object sender, Exception ex) 429 { 430 if (OnError != null) 431 OnError(sender, ex); 432 } 433 434 #region << Keepalive Timer functions >> CreateKeepAliveTimer()435 protected void CreateKeepAliveTimer() 436 { 437 // Create the delegate that invokes methods for the timer. 438 TimerCallback timerDelegate = new TimerCallback(KeepAliveTick); 439 int interval = m_KeepAliveInterval * 1000; 440 // Create a timer that waits x seconds, then invokes every x seconds. 441 m_KeepaliveTimer = new Timer(timerDelegate, null, interval, interval); 442 } 443 DestroyKeepAliveTimer()444 protected void DestroyKeepAliveTimer() 445 { 446 if (m_KeepaliveTimer == null) 447 return; 448 449 m_KeepaliveTimer.Dispose(); 450 m_KeepaliveTimer = null; 451 } 452 KeepAliveTick(Object state)453 private void KeepAliveTick(Object state) 454 { 455 // Send a Space for Keep Alive 456 Send(" "); 457 } 458 #endregion 459 } 460 }