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 agsXMPP.Net; 25 using agsXMPP.Xml.Dom; 26 using agsXMPP.protocol; 27 using agsXMPP.protocol.component; 28 using agsXMPP.Xml; 29 30 31 namespace agsXMPP 32 { 33 /// <summary> 34 /// <para> 35 /// use this class to write components that connect to a Jabebr/XMPP server 36 /// </para> 37 /// <para> 38 /// http://www.xmpp.org/extensions/xep-0114.html 39 /// </para> 40 /// </summary> 41 public class XmppComponentConnection : XmppConnection 42 { 43 // This route stuff is old undocumented jabberd(2) stuff. hopefully we can get rid of this one day 44 // or somebody writes up and XEP RouteHandler(object sender, Route r)45 public delegate void RouteHandler (object sender, Route r); 46 47 private bool m_CleanUpDone; 48 private bool m_StreamStarted; 49 50 #region << Constructors >> 51 /// <summary> 52 /// Creates a new Component Connection to a given server and port 53 /// </summary> XmppComponentConnection()54 public XmppComponentConnection() 55 { 56 m_IqGrabber = new IqGrabber(this); 57 } 58 59 /// <summary> 60 /// Creates a new Component Connection to a given server and port 61 /// </summary> 62 /// <param name="server">host/ip of the listening server</param> 63 /// <param name="port">port the server listens for the connection</param> XmppComponentConnection(string server, int port)64 public XmppComponentConnection(string server, int port) : this() 65 { 66 base.Server = server; 67 base.Port = port; 68 } 69 70 /// <summary> 71 /// Creates a new Component Connection to a given server, port and password (secret) 72 /// </summary> 73 /// <param name="server">host/ip of the listening server</param> 74 /// <param name="port">port the server listens for the connection</param> 75 /// <param name="password">password</param> XmppComponentConnection(string server, int port, string password)76 public XmppComponentConnection(string server, int port, string password) : this(server,port) 77 { 78 this.Password = password; 79 } 80 #endregion 81 82 #region << Properties and Member Variables >> 83 84 private string m_Password = null; 85 private bool m_Authenticated = false; 86 private Jid m_ComponentJid = null; 87 private IqGrabber m_IqGrabber = null; 88 89 public string Password 90 { 91 get { return m_Password; } 92 set { m_Password = value; } 93 } 94 95 /// <summary> 96 /// Are we Authenticated to the server? This is readonly and set by the library 97 /// </summary> 98 public bool Authenticated 99 { 100 get { return m_Authenticated; } 101 } 102 103 /// <summary> 104 /// The Domain of the component. 105 /// <para> 106 /// eg: <c>jabber.ag-software.de</c> 107 /// </para> 108 /// </summary> 109 public Jid ComponentJid 110 { 111 get { return m_ComponentJid; } 112 set { m_ComponentJid = value; } 113 } 114 115 public IqGrabber IqGrabber 116 { 117 get { return m_IqGrabber; } 118 set { m_IqGrabber = value; } 119 } 120 #endregion 121 122 #region << Events >> 123 // public event ErrorHandler OnError; 124 125 /// <summary> 126 /// connection is authenticated now and ready for receiving Route, Log and Xdb Packets 127 /// </summary> 128 public event ObjectHandler OnLogin; 129 130 public event ObjectHandler OnClose; 131 132 /// <summary> 133 /// handler for incoming routet packtes from the server 134 /// </summary> 135 public event RouteHandler OnRoute; 136 137 /// <summary> 138 /// Event that occurs on authentication errors 139 /// e.g. wrong password, user doesnt exist etc... 140 /// </summary> 141 public event XmppElementHandler OnAuthError; 142 143 /// <summary> 144 /// Stream errors <stream:error/> 145 /// </summary> 146 public event XmppElementHandler OnStreamError; 147 148 /// <summary> 149 /// Event occurs on Socket Errors 150 /// </summary> 151 public event ErrorHandler OnSocketError; 152 153 /// <summary> 154 /// 155 /// </summary> 156 public event IqHandler OnIq; 157 158 /// <summary> 159 /// We received a message. This could be a chat message, headline, normal message or a groupchat message. 160 /// There are also XMPP extension which are embedded in messages. 161 /// e.g. X-Data forms. 162 /// </summary> 163 public event MessageHandler OnMessage; 164 165 /// <summary> 166 /// We received a presence from a contact or chatroom. 167 /// Also subscriptions is handles in this event. 168 /// </summary> 169 public event PresenceHandler OnPresence; 170 171 #endregion 172 Open()173 public void Open() 174 { 175 _Open(); 176 } 177 178 /// <summary> 179 /// 180 /// </summary> 181 /// <param name="server"></param> 182 /// <param name="port"></param> Open(string server, int port)183 public void Open(string server, int port) 184 { 185 this.Server = server; 186 this.Port = port; 187 _Open(); 188 } 189 _Open()190 private void _Open() 191 { 192 m_CleanUpDone = false; 193 m_StreamStarted = false; 194 195 if (ConnectServer == null) 196 SocketConnect(base.Server, base.Port); 197 else 198 SocketConnect(this.ConnectServer, base.Port); 199 } 200 SendOpenStream()201 private void SendOpenStream() 202 { 203 // <stream:stream 204 // xmlns='jabber:component:accept' 205 // xmlns:stream='http://etherx.jabber.org/streams' 206 // to='shakespeare.lit'> 207 StringBuilder sb = new StringBuilder(); 208 209 //sb.Append("<?xml version='1.0'?>"); 210 sb.Append("<stream:stream"); 211 212 if (m_ComponentJid!=null) 213 sb.Append(" to='" + m_ComponentJid.ToString() + "'"); 214 215 sb.Append(" xmlns='" + Uri.ACCEPT + "'"); 216 sb.Append(" xmlns:stream='" + Uri.STREAM + "'"); 217 218 sb.Append(">"); 219 220 Open(sb.ToString()); 221 } 222 Login()223 private void Login() 224 { 225 // Send Handshake 226 Send( new Handshake(this.m_Password, this.StreamId) ); 227 } 228 229 #region << Stream Parser events >> StreamParserOnStreamStart(object sender, Node e)230 public override void StreamParserOnStreamStart(object sender, Node e) 231 { 232 base.StreamParserOnStreamStart (sender, e); 233 234 m_StreamStarted = true; 235 236 Login(); 237 } 238 StreamParserOnStreamEnd(object sender, Node e)239 public override void StreamParserOnStreamEnd(object sender, Node e) 240 { 241 base.StreamParserOnStreamEnd (sender, e); 242 243 if(!m_CleanUpDone) 244 CleanupSession(); 245 } 246 StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs)247 public override void StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs) 248 { 249 base.StreamParserOnStreamElement (sender, eventArgs); 250 var e = eventArgs.Element; 251 252 if (e is Handshake) 253 { 254 m_Authenticated = true; 255 256 if (OnLogin != null) 257 OnLogin(this); 258 259 if (KeepAlive) 260 CreateKeepAliveTimer(); 261 eventArgs.Handled = true; 262 } 263 else if (e is Route) 264 { 265 if (OnRoute != null) 266 OnRoute(this, e as Route); 267 eventArgs.Handled = true; 268 } 269 else if (e is protocol.Error) 270 { 271 protocol.Error streamErr = e as protocol.Error; 272 switch (streamErr.Condition) 273 { 274 // Auth errors are important for the users here, so throw catch auth errors 275 // in a separate event here 276 case agsXMPP.protocol.StreamErrorCondition.NotAuthorized: 277 // Authentication Error 278 if (OnAuthError != null) 279 OnAuthError(this, e as Element); 280 break; 281 default: 282 if (OnStreamError != null) 283 OnStreamError(this, e as Element); 284 break; 285 } 286 eventArgs.Handled = true; 287 } 288 else if (e is Message) 289 { 290 if (OnMessage != null) { 291 OnMessage(this, e as Message); 292 eventArgs.Handled = true; 293 } 294 } 295 else if (e is Presence) 296 { 297 if (OnPresence != null) { 298 OnPresence(this, e as Presence); 299 eventArgs.Handled = true; 300 } 301 } 302 else if (e is IQ) 303 { 304 if (OnIq != null) { 305 var iqEventArgs = new protocol.client.IQEventArgs((IQ)e); 306 OnIq(this, iqEventArgs); 307 if (iqEventArgs.Handled) { 308 eventArgs.Handled = true; 309 } 310 } 311 } 312 313 } 314 m_StreamParser_OnStreamError(object sender, Exception ex)315 private void m_StreamParser_OnStreamError(object sender, Exception ex) 316 { 317 if(!m_CleanUpDone) 318 CleanupSession(); 319 } 320 #endregion 321 322 #region << ClientSocket Events >> SocketOnConnect(object sender)323 public override void SocketOnConnect(object sender) 324 { 325 base.SocketOnConnect (sender); 326 327 SendOpenStream(); 328 } 329 SocketOnDisconnect(object sender)330 public override void SocketOnDisconnect(object sender) 331 { 332 base.SocketOnDisconnect (sender); 333 334 if(!m_CleanUpDone) 335 CleanupSession(); 336 } 337 SocketOnError(object sender, Exception ex)338 public override void SocketOnError(object sender, Exception ex) 339 { 340 base.SocketOnError(sender, ex); 341 342 if (m_StreamStarted && !m_CleanUpDone) 343 CleanupSession(); 344 345 if (OnSocketError != null) 346 OnSocketError(this, ex); 347 348 } 349 #endregion 350 Send(Element e)351 public override void Send(Element e) 352 { 353 // this is a hack to not send the xmlns="jabber:component:accept" with all packets 354 Element dummyEl = new Element("a"); 355 dummyEl.Namespace = Uri.ACCEPT; 356 357 dummyEl.AddChild(e); 358 string toSend = dummyEl.ToString(); 359 360 Send(toSend.Substring(35, toSend.Length - 35 - 4)); 361 } 362 CleanupSession()363 private void CleanupSession() 364 { 365 // This cleanup has only to be done if we were able to connect and teh XMPP Stream was started 366 DestroyKeepAliveTimer(); 367 m_CleanUpDone = true; 368 StreamParser.Reset(); 369 370 m_IqGrabber.Clear(); 371 372 if (OnClose!=null) 373 OnClose(this); 374 } 375 } 376 } 377