<commit_before>﻿/*
 * Copyright (C) 2003-2007 
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 *
 */
	 
package org.jivesoftware.xiff.core
{	 
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.XMLSocket;
	import flash.utils.Timer;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;
	
	import mx.logging.ILogger;
	
	import org.jivesoftware.xiff.auth.Anonymous;
	import org.jivesoftware.xiff.auth.External;
	import org.jivesoftware.xiff.auth.Plain;
	import org.jivesoftware.xiff.auth.SASLAuth;
	import org.jivesoftware.xiff.data.Extension;
	import org.jivesoftware.xiff.data.IExtension;
	import org.jivesoftware.xiff.data.IQ;
	import org.jivesoftware.xiff.data.Message;
	import org.jivesoftware.xiff.data.Presence;
	import org.jivesoftware.xiff.data.XMPPStanza;
	import org.jivesoftware.xiff.data.auth.AuthExtension;
	import org.jivesoftware.xiff.data.bind.BindExtension;
	import org.jivesoftware.xiff.data.forms.FormExtension;
	import org.jivesoftware.xiff.data.register.RegisterExtension;
	import org.jivesoftware.xiff.data.session.SessionExtension;
	import org.jivesoftware.xiff.events.*;
	import org.jivesoftware.xiff.exception.SerializationException;
	import org.jivesoftware.xiff.logging.LoggerFactory;

	/**
	 * Dispatched when a password change is successful.
	 * 
	 * @eventType org.jivesoftware.xiff.events.ChangePasswordSuccessEvent.PASSWORD_SUCCESS
	 */
    [Event(name="changePasswordSuccess", type="org.jivesoftware.xiff.events.ChangePasswordSuccessEvent")]
    
    /**
     * Dispatched when the connection is successfully made to the server.
     * 
     * @eventType org.jivesoftware.xiff.events.ConnectionSuccessEvent.CONNECT_SUCCESS
     */
    [Event(name="connection", type="org.jivesoftware.xiff.events.ConnectionSuccessEvent")]
    
    /**
     * Dispatched when there is a disconnection from the server.
     * 
     * @eventType org.jivesoftware.xiff.events.DisconnectionEvent.DISCONNECT
     */
    [Event(name="disconnection", type="org.jivesoftware.xiff.events.DisconnectionEvent")]
    
    /**
     * Dispatched when there is some type of XMPP error.
     * 
     * @eventType org.jivesoftware.xiff.events.XIFFErrorEvent.XIFF_ERROR
     */
    [Event(name="error", type="org.jivesoftware.xiff.events.XIFFErrorEvent")]
    
    /**
     * Dispatched whenever there is incoming XML data.
     * 
     * @eventType org.jivesoftware.xiff.events.IncomingDataEvent.INCOMING_DATA
     */
    [Event(name="incomingData", type="org.jivesoftware.xiff.events.IncomingDataEvent")]
    
    /**
     * Dispatched on successful authentication (login) with the server.
     * 
     * @eventType org.jivesoftware.xiff.events.LoginEvent.LOGIN
     */
    [Event(name="login", type="org.jivesoftware.xiff.events.LoginEvent")]
    
    /**
     * Dispatched on incoming messages.
     * 
     * @eventType org.jivesoftware.xiff.events.MessageEvent.MESSAGE
     */
    [Event(name="message", type="org.jivesoftware.xiff.events.MessageEvent")]
    
    /**
     * Dispatched whenever data is sent to the server.
     * 
     * @eventType org.jivesoftware.xiff.events.OutgoingDataEvent.OUTGOING_DATA
     */
    [Event(name="outgoingData", type="org.jivesoftware.xiff.events.OutgoingDataEvent")]
    
    /**
     * Dispatched on incoming presence data.
     * 
     * @eventType org.jivesoftware.xiff.events.PresenceEvent.PRESENCE
     */
    [Event(name="presence", type="org.jivesoftware.xiff.events.PresenceEvent")]
    
    /**
     * Dispatched on when new user account registration is successful.
     * 
     * @eventType org.jivesoftware.xiff.events.RegistrationSuccessEvent.REGISTRATION_SUCCESS
     */
    [Event(name="registrationSuccess", type="org.jivesoftware.xiff.events.RegistrationSuccessEvent")]
    
    /**
     * This class is used to connect to and manage data coming from an XMPP server. Use one instance
     * of this class per connection.
     */ 
	public class XMPPConnection extends EventDispatcher
	{
		private static const logger:ILogger = LoggerFactory.getLogger("org.jivesoftware.xiff.core.XMPPConnection");
		
		/**
		 * @private
		 */
		protected var _useAnonymousLogin:Boolean;
		
		/**
		 * @private
		 */
		protected var _socket:XMLSocket;
		
		/**
		 * @private
		 */
		protected var myServer:String;
		
		/**
		 * @private
		 */
	 	protected var myDomain:String;
		
		/**
		 * @private
		 */
		protected var myUsername:String;
		
		/**
		 * @private
		 */
		protected var myResource:String;
		
		/**
		 * @private
		 */
		protected var myPassword:String;
	
		/**
		 * @private
		 */
		protected var myPort:Number;
		
		/**
		 * @private
		 */
		protected var _active:Boolean;
		
		/**
		 * @private
		 */
		protected var loggedIn:Boolean;
		
		/**
		 * @private
		 */
		protected var ignoreWhitespace:Boolean;
		
		/**
		 * @private
		 */
		protected var openingStreamTag:String;
		
		/**
		 * @private
		 */
		protected var closingStreamTag:String;

		/**
		 * @private
		 */
		protected var sessionID:String;
		
		/**
		 * @private
		 */
		protected var pendingIQs:Object;
		
		/**
		 * @private
		 */
		protected var _expireTagSearch:Boolean;
		
		/**
		 * @private
		 */
		protected var auth:SASLAuth;
		
		protected static var _openConnections:Array = [];
		
		protected static var saslMechanisms:Object = {
			"PLAIN":Plain,
			"ANONYMOUS":Anonymous,
      "EXTERNAL":External
		};
		
		public function XMPPConnection()
		{	
			
			// Hash to hold callbacks for IQs
			pendingIQs = new Object();
			
			_useAnonymousLogin = false;
			active = false;
			loggedIn = false;
			ignoreWhitespace = true;
			resource = "xiff";
			port = 5222;
			
			AuthExtension.enable();
			BindExtension.enable();
			SessionExtension.enable();
			RegisterExtension.enable();
			FormExtension.enable();
		}
		
		/**
		 * Connects to the server.
		 *
		 * @param streamType (Optional) The type of initial stream negotiation, either &lt;flash:stream&gt; or &lt;stream:stream&gt;. 
		 * Some servers, like Jabber, Inc.'s XCP and Jabberd 1.4 expect &lt;flash:stream&gt; from a Flash client instead of the standard &lt;stream:stream&gt;.
		 * The options for this parameter are: "flash", "terminatedFlash", "standard" and "terminatedStandard". The default is "terminatedStandard".
		 *
		 * @return A boolean indicating whether the server was found.
		 */
		public function connect( streamType:String = "terminatedStandard" ):Boolean
		{
			
			// Create the socket
			_socket = _createXmlSocket(); 
			
			active = false;
			loggedIn = false;
			
			// Stream type lets user set opening/closing tag - some servers (jadc2s) prefer <stream:flash> to the standard
			// <stream:stream>
			switch( streamType ) {
				case "flash":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\">" );
					closingStreamTag = new String( "</flash:stream>" );
					break;
					
				case "terminatedFlash":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\" />" );
					closingStreamTag = new String( "</flash:stream>" );
					break;
					
				case "standard":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">" );
					closingStreamTag = new String( "</stream:stream>" );
					break;
			
				case "terminatedStandard":
				default:
					openingStreamTag = new String( "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" />" );
					closingStreamTag = new String( "</stream:stream>" );
					break;
			}
			_socket.connect( server, port );
			return true;
		}
		
		public static function registerSASLMechanism(name:String, authClass:Class):void
		{
			saslMechanisms[name] = authClass;
		}
		
		public static function disableSASLMechanism(name:String):void
		{
			saslMechanisms[name] = null;
		}
		
		/**
		 * Disconnects from the server if currently connected. After disconnect, 
		 * a <code>DisconnectionEvent.DISCONNECT</code> event is broadcast.
		 */
		public function disconnect():void
		{
			if( isActive() ) {
				sendXML( closingStreamTag );
				if(_socket)
					_socket.close();
				active = false;
				loggedIn = false;
				var event:DisconnectionEvent = new DisconnectionEvent();
				dispatchEvent(event);
			}
		}
		
		/**
		 * Sends data to the server. If the data to send cannot be serialized properly, this method throws a <code>SerializeException</code>.
		 *
		 * @param o The data to send. This must be an instance of a class that implements the ISerializable interface.
		 * @see org.jivesoftware.xiff.data.ISerializable
		 * @example The following example sends a basic chat message to the user with the JID "<EMAIL>".<br />
		 * <pre>var msg:Message = new Message( "<EMAIL>", null, "Hi Bob.", "<b>Hi Bob.</b>", Message.CHAT_TYPE );
		 * myXMPPConnection.send( msg );</pre>
		 */
		public function send( o:XMPPStanza ):void
		{
			if( isActive() ) {
				if( o is IQ ) {
	                var iq:IQ = o as IQ;
	                if ((iq.callbackName != null && iq.callbackScope != null) || iq.callback != null)
	                {
	                	addIQCallbackToPending( iq.id, iq.callbackName, iq.callbackScope, iq.callback );
	                }		
				}
				var root:XMLNode = o.getNode().parentNode;
				if (root == null) {
					root = new XMLDocument();
				}
	
				if (o.serialize(root)) {
					sendXML( root.firstChild );
				} else {
					throw new SerializationException();
				}
			}
		}
		
		public function sendKeepAlive():void
		{
			if( isActive() ) {
				sendXML(" ");
			}
		}
		
		/**
		 * Determines whether the connection with the server is currently active. (Not necessarily logged in.
		 * For login status, use the <code>isLoggedIn()</code> method.)
		 * 
		 * @return A boolean indicating whether the connection is active.
		 * @see org.jivesoftware.xiff.core.XMPPConnection#isLoggedIn
		 */
		public function isActive():Boolean
		{
			return active;
		}
		
		/**
		 * Determines whether the user is connected and logged into the server.
		 * 
		 * @return A boolean indicating whether the user is logged in.
		 * @see org.jivesoftware.xiff.core.XMPPConnection#isActive
		 */
		public function isLoggedIn():Boolean
		{
			return loggedIn;
		}
		
		/**
		 * Issues a request for the information that must be submitted for registration with the server.
		 * When the data returns, a <code>RegistrationFieldsEvent.REG_FIELDS</code> event is dispatched 
		 * containing the requested data.
		 */
		public function getRegistrationFields():void
		{
			var regIQ:IQ = new IQ( new EscapedJID(domain), IQ.GET_TYPE, XMPPStanza.generateID("reg_info_"), "getRegistrationFields_result", this, null);
			regIQ.addExtension(new RegisterExtension(regIQ.getNode()));
	
			send( regIQ );
		}
		
		/**
		 * Registers a new account with the server, sending the registration data as specified in the fieldMap paramter.
		 *
		 * @param fieldMap An object map containing the data to use for registration. The map should be composed of 
		 * attribute:value pairs for each registration data item.
		 * @param key (Optional) If a key was passed in the "data" field of the "registrationFields" event, 
		 * that key must also be passed here.
		 * required field needed for registration.
		 */
		public function sendRegistrationFields( fieldMap:Object, key:String ):void
		{
			var regIQ:IQ = new IQ( new EscapedJID(domain), IQ.SET_TYPE, XMPPStanza.generateID("reg_attempt_"), "sendRegistrationFields_result", this, null );
			var ext:RegisterExtension = new RegisterExtension(regIQ.getNode());
	
			for( var i:String in fieldMap ) {
				ext[i] = fieldMap[i];
			}
			if (key != null) {
				ext.key = key;
			}
	
			regIQ.addExtension(ext);
			send( regIQ );
		}
		
		/**
		 * Changes the user's account password on the server. If the password change is successful, 
		 * the class will broadcast a <code>ChangePasswordSuccessEvent.PASSWORD_SUCCESS</code> event.
		 *
		 * @param newPassword The <PASSWORD>
		 */
		public function changePassword( newPassword:String ):void
		{
			var passwdIQ:IQ = new IQ( new EscapedJID(domain), IQ.SET_TYPE, XMPPStanza.generateID("pswd_change_"), "changePassword_result", this, null );
			var ext:RegisterExtension = new RegisterExtension(passwdIQ.getNode());
	
			ext.username = jid.escaped.bareJID;
			ext.password = <PASSWORD>;
	
			passwdIQ.addExtension(ext);
			send( passwdIQ );
		}
		
		/**
		 * Gets the fully qualified JID (user@server/resource) of the user. A fully-qualified JID includes 
		 * the resource. A bare JID does not. To get the bare JID, use the <code>getBareJID()</code> method.
		 *
		 * @return The fully qualified JID
		 * @see #getBareJID
		 */
		public function get jid():UnescapedJID
		{
			return new UnescapedJID(myUsername + "@" + myDomain + "/" + myResource);
		}
		
		/**
		 * @private
		 */
		protected function changePassword_result( resultIQ:IQ ):void
		{
			if( resultIQ.type == IQ.RESULT_TYPE ) {
				var event:ChangePasswordSuccessEvent = new ChangePasswordSuccessEvent();
				dispatchEvent(event);
			}
			else {
				// We weren't expecting this
				dispatchError( "unexpected-request", "Unexpected Request", "wait", 400 );
			}
		}
		
		/**
		 * @private
		 */
		protected function getRegistrationFields_result( resultIQ:IQ ):void
		{
			try
			{
				var ext:RegisterExtension = resultIQ.getAllExtensionsByNS(RegisterExtension.NS)[0];
				var fields:Array = ext.getRequiredFieldNames(); //TODO, phase this out
				
				var event:RegistrationFieldsEvent = new RegistrationFieldsEvent();
				event.fields = fields;
				event.data = ext;
			}
			catch (e:Error)
			 {
			 	trace(e.getStackTrace());
			 }
		}
		
		/**
		 * @private
		 */
		protected function sendRegistrationFields_result( resultIQ:IQ ):void
		{<commit_msg>XIFF-38: Made XMPPConnection throw event from getRegistrationFields_result<commit_after>﻿/*
 * Copyright (C) 2003-2007 
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 * <NAME> <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 *
 */
	 
package org.jivesoftware.xiff.core
{	 
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.XMLSocket;
	import flash.utils.Timer;
	import flash.xml.XMLDocument;
	import flash.xml.XMLNode;
	
	import mx.logging.ILogger;
	
	import org.jivesoftware.xiff.auth.Anonymous;
	import org.jivesoftware.xiff.auth.External;
	import org.jivesoftware.xiff.auth.Plain;
	import org.jivesoftware.xiff.auth.SASLAuth;
	import org.jivesoftware.xiff.data.Extension;
	import org.jivesoftware.xiff.data.IExtension;
	import org.jivesoftware.xiff.data.IQ;
	import org.jivesoftware.xiff.data.Message;
	import org.jivesoftware.xiff.data.Presence;
	import org.jivesoftware.xiff.data.XMPPStanza;
	import org.jivesoftware.xiff.data.auth.AuthExtension;
	import org.jivesoftware.xiff.data.bind.BindExtension;
	import org.jivesoftware.xiff.data.forms.FormExtension;
	import org.jivesoftware.xiff.data.register.RegisterExtension;
	import org.jivesoftware.xiff.data.session.SessionExtension;
	import org.jivesoftware.xiff.events.*;
	import org.jivesoftware.xiff.exception.SerializationException;
	import org.jivesoftware.xiff.logging.LoggerFactory;

	/**
	 * Dispatched when a password change is successful.
	 * 
	 * @eventType org.jivesoftware.xiff.events.ChangePasswordSuccessEvent.PASSWORD_SUCCESS
	 */
    [Event(name="changePasswordSuccess", type="org.jivesoftware.xiff.events.ChangePasswordSuccessEvent")]
    
    /**
     * Dispatched when the connection is successfully made to the server.
     * 
     * @eventType org.jivesoftware.xiff.events.ConnectionSuccessEvent.CONNECT_SUCCESS
     */
    [Event(name="connection", type="org.jivesoftware.xiff.events.ConnectionSuccessEvent")]
    
    /**
     * Dispatched when there is a disconnection from the server.
     * 
     * @eventType org.jivesoftware.xiff.events.DisconnectionEvent.DISCONNECT
     */
    [Event(name="disconnection", type="org.jivesoftware.xiff.events.DisconnectionEvent")]
    
    /**
     * Dispatched when there is some type of XMPP error.
     * 
     * @eventType org.jivesoftware.xiff.events.XIFFErrorEvent.XIFF_ERROR
     */
    [Event(name="error", type="org.jivesoftware.xiff.events.XIFFErrorEvent")]
    
    /**
     * Dispatched whenever there is incoming XML data.
     * 
     * @eventType org.jivesoftware.xiff.events.IncomingDataEvent.INCOMING_DATA
     */
    [Event(name="incomingData", type="org.jivesoftware.xiff.events.IncomingDataEvent")]
    
    /**
     * Dispatched on successful authentication (login) with the server.
     * 
     * @eventType org.jivesoftware.xiff.events.LoginEvent.LOGIN
     */
    [Event(name="login", type="org.jivesoftware.xiff.events.LoginEvent")]
    
    /**
     * Dispatched on incoming messages.
     * 
     * @eventType org.jivesoftware.xiff.events.MessageEvent.MESSAGE
     */
    [Event(name="message", type="org.jivesoftware.xiff.events.MessageEvent")]
    
    /**
     * Dispatched whenever data is sent to the server.
     * 
     * @eventType org.jivesoftware.xiff.events.OutgoingDataEvent.OUTGOING_DATA
     */
    [Event(name="outgoingData", type="org.jivesoftware.xiff.events.OutgoingDataEvent")]
    
    /**
     * Dispatched on incoming presence data.
     * 
     * @eventType org.jivesoftware.xiff.events.PresenceEvent.PRESENCE
     */
    [Event(name="presence", type="org.jivesoftware.xiff.events.PresenceEvent")]
    
    /**
     * Dispatched on when new user account registration is successful.
     * 
     * @eventType org.jivesoftware.xiff.events.RegistrationSuccessEvent.REGISTRATION_SUCCESS
     */
    [Event(name="registrationSuccess", type="org.jivesoftware.xiff.events.RegistrationSuccessEvent")]
    
    /**
     * This class is used to connect to and manage data coming from an XMPP server. Use one instance
     * of this class per connection.
     */ 
	public class XMPPConnection extends EventDispatcher
	{
		private static const logger:ILogger = LoggerFactory.getLogger("org.jivesoftware.xiff.core.XMPPConnection");
		
		/**
		 * @private
		 */
		protected var _useAnonymousLogin:Boolean;
		
		/**
		 * @private
		 */
		protected var _socket:XMLSocket;
		
		/**
		 * @private
		 */
		protected var myServer:String;
		
		/**
		 * @private
		 */
	 	protected var myDomain:String;
		
		/**
		 * @private
		 */
		protected var myUsername:String;
		
		/**
		 * @private
		 */
		protected var myResource:String;
		
		/**
		 * @private
		 */
		protected var myPassword:String;
	
		/**
		 * @private
		 */
		protected var myPort:Number;
		
		/**
		 * @private
		 */
		protected var _active:Boolean;
		
		/**
		 * @private
		 */
		protected var loggedIn:Boolean;
		
		/**
		 * @private
		 */
		protected var ignoreWhitespace:Boolean;
		
		/**
		 * @private
		 */
		protected var openingStreamTag:String;
		
		/**
		 * @private
		 */
		protected var closingStreamTag:String;

		/**
		 * @private
		 */
		protected var sessionID:String;
		
		/**
		 * @private
		 */
		protected var pendingIQs:Object;
		
		/**
		 * @private
		 */
		protected var _expireTagSearch:Boolean;
		
		/**
		 * @private
		 */
		protected var auth:SASLAuth;
		
		protected static var _openConnections:Array = [];
		
		protected static var saslMechanisms:Object = {
			"PLAIN":Plain,
			"ANONYMOUS":Anonymous,
      "EXTERNAL":External
		};
		
		public function XMPPConnection()
		{	
			
			// Hash to hold callbacks for IQs
			pendingIQs = new Object();
			
			_useAnonymousLogin = false;
			active = false;
			loggedIn = false;
			ignoreWhitespace = true;
			resource = "xiff";
			port = 5222;
			
			AuthExtension.enable();
			BindExtension.enable();
			SessionExtension.enable();
			RegisterExtension.enable();
			FormExtension.enable();
		}
		
		/**
		 * Connects to the server.
		 *
		 * @param streamType (Optional) The type of initial stream negotiation, either &lt;flash:stream&gt; or &lt;stream:stream&gt;. 
		 * Some servers, like Jabber, Inc.'s XCP and Jabberd 1.4 expect &lt;flash:stream&gt; from a Flash client instead of the standard &lt;stream:stream&gt;.
		 * The options for this parameter are: "flash", "terminatedFlash", "standard" and "terminatedStandard". The default is "terminatedStandard".
		 *
		 * @return A boolean indicating whether the server was found.
		 */
		public function connect( streamType:String = "terminatedStandard" ):Boolean
		{
			
			// Create the socket
			_socket = _createXmlSocket(); 
			
			active = false;
			loggedIn = false;
			
			// Stream type lets user set opening/closing tag - some servers (jadc2s) prefer <stream:flash> to the standard
			// <stream:stream>
			switch( streamType ) {
				case "flash":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\">" );
					closingStreamTag = new String( "</flash:stream>" );
					break;
					
				case "terminatedFlash":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><flash:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:flash=\"http://www.jabber.com/streams/flash\" version=\"1.0\" />" );
					closingStreamTag = new String( "</flash:stream>" );
					break;
					
				case "standard":
					openingStreamTag = new String( "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">" );
					closingStreamTag = new String( "</stream:stream>" );
					break;
			
				case "terminatedStandard":
				default:
					openingStreamTag = new String( "<?xml version=\"1.0\"?><stream:stream to=\"" + domain + "\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" />" );
					closingStreamTag = new String( "</stream:stream>" );
					break;
			}
			_socket.connect( server, port );
			return true;
		}
		
		public static function registerSASLMechanism(name:String, authClass:Class):void
		{
			saslMechanisms[name] = authClass;
		}
		
		public static function disableSASLMechanism(name:String):void
		{
			saslMechanisms[name] = null;
		}
		
		/**
		 * Disconnects from the server if currently connected. After disconnect, 
		 * a <code>DisconnectionEvent.DISCONNECT</code> event is broadcast.
		 */
		public function disconnect():void
		{
			if( isActive() ) {
				sendXML( closingStreamTag );
				if(_socket)
					_socket.close();
				active = false;
				loggedIn = false;
				var event:DisconnectionEvent = new DisconnectionEvent();
				dispatchEvent(event);
			}
		}
		
		/**
		 * Sends data to the server. If the data to send cannot be serialized properly, this method throws a <code>SerializeException</code>.
		 *
		 * @param o The data to send. This must be an instance of a class that implements the ISerializable interface.
		 * @see org.jivesoftware.xiff.data.ISerializable
		 * @example The following example sends a basic chat message to the user with the JID "<EMAIL>".<br />
		 * <pre>var msg:Message = new Message( "<EMAIL>", null, "Hi Bob.", "<b>Hi Bob.</b>", Message.CHAT_TYPE );
		 * myXMPPConnection.send( msg );</pre>
		 */
		public function send( o:XMPPStanza ):void
		{
			if( isActive() ) {
				if( o is IQ ) {
	                var iq:IQ = o as IQ;
	                if ((iq.callbackName != null && iq.callbackScope != null) || iq.callback != null)
	                {
	                	addIQCallbackToPending( iq.id, iq.callbackName, iq.callbackScope, iq.callback );
	                }		
				}
				var root:XMLNode = o.getNode().parentNode;
				if (root == null) {
					root = new XMLDocument();
				}
	
				if (o.serialize(root)) {
					sendXML( root.firstChild );
				} else {
					throw new SerializationException();
				}
			}
		}
		
		public function sendKeepAlive():void
		{
			if( isActive() ) {
				sendXML(" ");
			}
		}
		
		/**
		 * Determines whether the connection with the server is currently active. (Not necessarily logged in.
		 * For login status, use the <code>isLoggedIn()</code> method.)
		 * 
		 * @return A boolean indicating whether the connection is active.
		 * @see org.jivesoftware.xiff.core.XMPPConnection#isLoggedIn
		 */
		public function isActive():Boolean
		{
			return active;
		}
		
		/**
		 * Determines whether the user is connected and logged into the server.
		 * 
		 * @return A boolean indicating whether the user is logged in.
		 * @see org.jivesoftware.xiff.core.XMPPConnection#isActive
		 */
		public function isLoggedIn():Boolean
		{
			return loggedIn;
		}
		
		/**
		 * Issues a request for the information that must be submitted for registration with the server.
		 * When the data returns, a <code>RegistrationFieldsEvent.REG_FIELDS</code> event is dispatched 
		 * containing the requested data.
		 */
		public function getRegistrationFields():void
		{
			var regIQ:IQ = new IQ( new EscapedJID(domain), IQ.GET_TYPE, XMPPStanza.generateID("reg_info_"), "getRegistrationFields_result", this, null);
			regIQ.addExtension(new RegisterExtension(regIQ.getNode()));
	
			send( regIQ );
		}
		
		/**
		 * Registers a new account with the server, sending the registration data as specified in the fieldMap paramter.
		 *
		 * @param fieldMap An object map containing the data to use for registration. The map should be composed of 
		 * attribute:value pairs for each registration data item.
		 * @param key (Optional) If a key was passed in the "data" field of the "registrationFields" event, 
		 * that key must also be passed here.
		 * required field needed for registration.
		 */
		public function sendRegistrationFields( fieldMap:Object, key:String ):void
		{
			var regIQ:IQ = new IQ( new EscapedJID(domain), IQ.SET_TYPE, XMPPStanza.generateID("reg_attempt_"), "sendRegistrationFields_result", this, null );
			var ext:RegisterExtension = new RegisterExtension(regIQ.getNode());
	
			for( var i:String in fieldMap ) {
				ext[i] = fieldMap[i];
			}
			if (key != null) {
				ext.key = key;
			}
	
			regIQ.addExtension(ext);
			send( regIQ );
		}
		
		/**
		 * Changes the user's account password on the server. If the password change is successful, 
		 * the class will broadcast a <code>ChangePasswordSuccessEvent.PASSWORD_SUCCESS</code> event.
		 *
		 * @param newPassword The new password
		 */
		public function changePassword( newPassword:String ):void
		{
			var passwdIQ:IQ = new IQ( new EscapedJID(domain), IQ.SET_TYPE, XMPPStanza.generateID("pswd_change_"), "changePassword_result", this, null );
			var ext:RegisterExtension = new RegisterExtension(passwdIQ.getNode());
	
			ext.username = jid.escaped.bareJID;
			ext.password = <PASSWORD>;
	
			passwdIQ.addExtension(ext);
			send( passwdIQ );
		}
		
		/**
		 * Gets the fully qualified JID (user@server/resource) of the user. A fully-qualified JID includes 
		 * the resource. A bare JID does not. To get the bare JID, use the <code>getBareJID()</code> method.
		 *
		 * @return The fully qualified JID
		 * @see #getBareJID
		 */
		public function get jid():UnescapedJID
		{
			return new UnescapedJID(myUsername + "@" + myDomain + "/" + myResource);
		}
		
		/**
		 * @private
		 */
		protected function changePassword_result( resultIQ:IQ ):void
		{
			if( resultIQ.type == IQ.RESULT_TYPE ) {
				var event:ChangePasswordSuccessEvent = new ChangePasswordSuccessEvent();
				dispatchEvent(event);
			}
			else {
				// We weren't expecting this
				dispatchError( "unexpected-request", "Unexpected Request", "wait", 400 );
			}
		}
		
		/**
		 * @private
		 */
		protected function getRegistrationFields_result( resultIQ:IQ ):void
		{
			try
			{
				var ext:RegisterExtension = resultIQ.getAllExtensionsByNS(RegisterExtension.NS)[0];
				var fields:Array = ext.getRequiredFieldNames(); //TODO, phase this out
				
				var event:RegistrationFieldsEvent = new RegistrationFieldsEvent();
				event.fields = fields;
				event.data = ext;
				
				dispatchEvent(event);
			}
			catch (e:Error)
			 {
			 	trace(e.getStackTrace());
			 }
		}
		
		/**
		 * @private
		 */
		protected function sendRegistrationFields_result( resultIQ:IQ ):void
		{
{'fraction_non_alphanumeric': 0.12000399015761122, 'fraction_numerical': 0.0034581365963955576, 'mean_word_length': 3.1986597794220297, 'pattern_counts': {'":': 14, '<': 96, '<?xml version=': 8, '>': 96, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 28}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4932563', 'n_tokens_mistral': 10787, 'n_tokens_neox': 9057, 'n_words': 2314}