 		{
 			var tempPresence:Presence = new Presence( tojid.escaped, null, Presence.SUBSCRIBED_TYPE );
 			_connection.send( tempPresence );
-			
+
 			// Request a return subscription
 			if ( requestAfterGrant )
 			{
 				requestSubscription( tojid, true );
 			}
 		}
-		
-		/**
-		 * Revokes an existing presence subscription or denies a subscription request. If a user
-		 * has sent you a subscription request you can use this method to deny that request. Otherwise,
-		 * if a user already has a granted presence subscription, you can use this method to revoke that
-		 * subscription.
-		 *
-		 * @param	tojid The JID of the user or service that you are denying subscription
-		 */
-		public function denySubscription( tojid:UnescapedJID ):void
-		{
-			var tempPresence:Presence = new Presence( tojid.escaped, null, Presence.UNSUBSCRIBED_TYPE );
-			_connection.send( tempPresence );
-		}
-		
-		/**
-		 * Updates the information for an existing contact. You can use this method to change the
-		 * display name or associated group for a contact in your roster.
-		 *
-		 * @param	rosterItem The value object of the contact to update
-		 * @param	newName The new display name for this contact
-		 * @param	newGroups The new groups to associate the contact with
-		 */
-		private function updateContact( rosterItem:RosterItemVO, newName:String, groupNames:Array ):void
-		{
-			var tempIQ:IQ = new IQ( null, IQ.SET_TYPE, XMPPStanza.generateID("update_contact_") );
-			var ext:RosterExtension = new RosterExtension( tempIQ.getNode() );
-			
-			ext.addItem( rosterItem.jid.escaped, rosterItem.subscribeType, newName, groupNames );
-			tempIQ.addExtension( ext );
-			_connection.send( tempIQ );
-		}
-		
-		/**
-		 * Updates the display name for an existing contact.
-		 *
-		 * @param	rosterItem The value object of the contact to update
-		 * @param	newName The new display name for this contact
-		 */
-		public function updateContactName( rosterItem:RosterItemVO, newName:String ):void
-		{
-			var groupNames:Array = [];
-			for each(var group:RosterGroup in getContainingGroups(rosterItem))
-			{
-				groupNames.push(group.label);
-			}
-			updateContact( rosterItem, newName, groupNames);
-		}
-		
-		/**
-		 *
-		 * @param	item
-		 * @return
-		 */
-		public function getContainingGroups(item:RosterItemVO):Array
-		{
-			var result:Array = [];
-			for each(var group:RosterGroup in _groups)
-			{
-				if (group.contains(item))
-				{
-					result.push(group);
-				}
-			}
-			return result;
-		}
-		
-		/**
-		 * Updates the groups associated with an existing contact.
-		 *
-		 * @param	rosterItem The value object of the contact to update
-		 * @param	newGroups The new groups to associate the contact with
-		 */
-		public function updateContactGroups( rosterItem:RosterItemVO, newGroupNames:Array ):void
-		{
-			updateContact( rosterItem, rosterItem.displayName, newGroupNames );
-		}
-		
+
+		/**
+		 * Removes a contact from the roster and revokes all presence subscriptions for that contact.
+		 * This method will only attempt action if the contact you are trying to remove is currently on the
+		 * roster in the first place.
+		 *
+		 * @param	rosterItem The value object which is to be removed
+		 */
+		public function removeContact( rosterItem:RosterItemVO ):void
+		{
+			if ( contains( rosterItem ) )
+			{
+				var tempIQ:IQ = new IQ( null, IQ.SET_TYPE, XMPPStanza.generateID( "remove_user_" ),
+										"unsubscribe_result", this );
+				var ext:RosterExtension = new RosterExtension( tempIQ.getNode() );
+				ext.addItem( new EscapedJID( rosterItem.jid.bareJID ), RosterExtension.SUBSCRIBE_TYPE_REMOVE );
+				tempIQ.addExtension( ext );
+				_connection.send( tempIQ );
+
+				//the roster item is not actually removed from the roster
+				//until confirmation comes back from the XMPP server
+			}
+		}
+
+		/**
+		 * Requests subscription authorization with a user or service. In the XMPP-world, you cannot receive
+		 * notifications on changes to a contact's presence until that contact has authorized you to subscribe
+		 * to his/her/its presence.
+		 *
+		 * @param	id The JID of the user or service that you wish to subscribe to
+		 * @param	isResponse
+		 * @see	#subscriptionDenial
+		 */
+		public function requestSubscription( id:UnescapedJID, isResponse:Boolean = false ):void
+		{
+			// Roster length is 0 if it a response to a user request. we must handle this event.
+			var presence:Presence;
+			if ( isResponse )
+			{
+				presence = new Presence( id.escaped, null, Presence.SUBSCRIBE_TYPE );
+				_connection.send( presence );
+				return;
+			}
+			// Only request for items in the roster
+			if ( contains( RosterItemVO.get( id, false ) ) )
+			{
+				presence = new Presence( id.escaped, null, Presence.SUBSCRIBE_TYPE );
+				_connection.send( presence );
+			}
+		}
+
 		/**
 		 * Sets your current presence status. Calling this method notifies others who
 		 * are subscribed to your presence of a presence change. You should use this to

		 */
		public function fetchRoster():void
		{
			var tempIQ:IQ = new IQ( null, IQ.GET_TYPE, XMPPStanza.generateID("roster_"), "fetchRoster_result", this );
			tempIQ.addExtension( new RosterExtension( tempIQ.getNode() ) );
			_connection.send( tempIQ );
		}
		
		/**
		 * Grants a user or service authorization for subscribing to your presence. Once authorization
		 * is granted, the user can see whether you are offline, online, away, etc. Subscriptions can

		 */
		public function setPresence( show:String, status:String, priority:Number ):void
		{
			var presence:Presence = new Presence( null, null, null, show, status,
													  priority );
			_connection.send( presence );
		}

		/**
		 *
		 * @param	resultIQ
		 */
		public function unsubscribe_result( resultIQ:IQ ):void
		{
			// Does nothing for now
		}

		/**
		 * Updates the groups associated with an existing contact.
		 *
		 * @param	rosterItem The value object of the contact to update
		 * @param	newGroups The new groups to associate the contact with
		 */
		public function updateContactGroups( rosterItem:RosterItemVO, newGroupNames:Array ):void
		{
			updateContact( rosterItem, rosterItem.displayName, newGroupNames );
		}

		/**
		 * Updates the display name for an existing contact.
		 *
		 * @param	rosterItem The value object of the contact to update
		 * @param	newName The new display name for this contact
		 */
		public function updateContactName( rosterItem:RosterItemVO, newName:String ):void
		{
			var groupNames:Array = [];
			for each ( var group:RosterGroup in getContainingGroups( rosterItem ) )
			{
				groupNames.push( group.label );
			}
			updateContact( rosterItem, newName, groupNames );
		}

		private function addRosterItem( jid:UnescapedJID, displayName:String, show:String, status:String, groupNames:Array, type:String, askType:String = "none" ):Boolean
		{
			if ( !jid )
			{
				return false;
			}

			var rosterItem:RosterItemVO = RosterItemVO.get( jid, true );
			if ( !contains( rosterItem ) )
			{
				addItem( rosterItem );
			}
			if ( displayName )
			{
				rosterItem.displayName = displayName;
			}
			rosterItem.subscribeType = type;
			rosterItem.askType = askType;
			rosterItem.status = status;
			rosterItem.show = show;
			setContactGroups( rosterItem, groupNames );

			var event:RosterEvent = new RosterEvent( RosterEvent.USER_ADDED );
			event.jid = jid;
			event.data = rosterItem;
			dispatchEvent( event );

			return true;
		}

		/**
		 *
		 * @param	eventObj PresenceEvent, LoginEvent or RosterExtension
