void ModifyContactListTask::processFolderChange( Field::MultiField * container )
{
	if ( !( container->method() == NMFIELD_METHOD_ADD
			|| container->method() == NMFIELD_METHOD_DELETE ) )
		return;

	client()->debug( "ModifyContactListTask::processFolderChange()" );
	FolderItem folder;
	Field::SingleField * current;
	Field::FieldList fl = container->fields();
	// object id
	current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
	folder.id = current->value().toInt();
	// sequence number
	current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
	folder.sequence = current->value().toInt();
	// name 
	current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
	folder.name = current->value().toString();
	// parent
	current = fl.findSingleField( NM_A_SZ_PARENT_ID );
	folder.parentId = current->value().toInt();
	if ( container->method() == NMFIELD_METHOD_ADD )
		emit gotFolderAdded( folder );
	else if ( container->method() == NMFIELD_METHOD_DELETE )
		emit gotFolderDeleted( folder );
	
}
void ModifyContactListTask::processContactChange( Field::MultiField * container )
{
	if ( !( container->method() == NMFIELD_METHOD_ADD
			|| container->method() == NMFIELD_METHOD_DELETE ) )
		return;

	client()->debug( "ModifyContactListTask::processContactChange()" );
	Field::SingleField * current;
	Field::FieldList fl = container->fields();
	ContactItem contact;
	current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
	contact.id = current->value().toInt();
	current = fl.findSingleField( NM_A_SZ_PARENT_ID );
	contact.parentId = current->value().toInt();
	current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
	contact.sequence = current->value().toInt();
	current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
	contact.displayName = current->value().toString();
	current = fl.findSingleField( NM_A_SZ_DN );
	contact.dn = current->value().toString();
	
	if ( container->method() == NMFIELD_METHOD_ADD )
		emit gotContactAdded( contact );
	else if ( container->method() == NMFIELD_METHOD_DELETE )
		emit gotContactDeleted( contact );
}
bool GetStatusTask::take( Transfer * transfer )
{
	if ( !forMe( transfer ) )
		return false;
	Response * response = dynamic_cast<Response *>( transfer );
	if ( !response )
		return false;
	
	Field::FieldList responseFields = response->fields();
	responseFields.dump( true );
	// parse received details and signal like billio
	Field::SingleField * sf = 0;
	Q_UINT16 status;
	sf = responseFields.findSingleField( NM_A_SZ_STATUS );
	if ( sf )
	{
		// As of Sept 2004 the server always responds with 2 (Available) here, even if the sender is not
		// This must be because the sender is not on our contact list but has sent us a message.
		// TODO: Check that the change to sending DNs above has fixed this problem.
		status = sf->value().toInt();
		// unfortunately getstatus doesn't give us an away message so we pass QString::null here
		emit gotStatus( m_userDN, status, QString::null );
		setSuccess();
	}	
	else
		setError();	
	return true;
}
bool PollSearchResultsTask::take( Transfer * transfer )
{
	if ( !forMe( transfer ) )
		return false;
	Response * response = dynamic_cast<Response *>( transfer );
	if ( !response )
		return false;
	if ( response->resultCode() )
	{
		setError( response->resultCode() );
		return true;
	}
	
	// look for the status code
	Field::FieldList responseFields = response->fields();
	Field::SingleField * sf = responseFields.findSingleField( Field::NM_A_SZ_STATUS );
	m_queryStatus = sf->value().toInt();
	
	Field::MultiField * resultsArray = responseFields.findMultiField( Field::NM_A_FA_RESULTS );
	if ( !resultsArray )
	{
		setError( Protocol );
		return true;
	}
	Field::FieldList matches = resultsArray->fields();
	const Field::FieldListIterator end = matches.end();
	for ( Field::FieldListIterator it = matches.find( Field::NM_A_FA_CONTACT );
		  it != end;
		  it = matches.find( ++it, Field::NM_A_FA_CONTACT ) )
	{
		Field::MultiField * mf = static_cast<Field::MultiField *>( *it );
		Field::FieldList contact = mf->fields();
		GroupWise::ContactDetails cd = extractUserDetails( contact );
		m_results.append( cd );
	}
	
	// first field: Field::NM_A_SZ_STATUS contains 
	#define SEARCH_PENDING 0
	#define SEARCH_INPROGRESS 1
	#define SEARCH_COMPLETED 2
	#define SEARCH_TIMEOUT 3
	#define SEARCH_CANCELLED 4
	#define SEARCH_ERROR 5
	// set a status code if needed
	// followed by Field::NM_A_FA_RESULTS, looks like a getdetails
	// add an accessor to get at the results list of ContactItems, probably
	
	if ( m_queryStatus != 2 )
		setError( m_queryStatus );
	else
		setSuccess( m_queryStatus );
	return true;
}
bool ChatPropertiesTask::take( Transfer * transfer )
{
	if ( !forMe( transfer ) )
		return false;
	Response * response = dynamic_cast<Response *>( transfer );
	if ( !response )
		return false;
	if ( response->resultCode() )
	{
		setError( response->resultCode() );
		return true;
	}
	
	Field::FieldList responseFields = response->fields();
	Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_CHAT );
	if ( !resultsArray )
	{
		setError( Protocol );
		return true;
	}
	
	Field::FieldList lst = resultsArray->fields();
	const Field::FieldListIterator end = lst.end();
	for ( Field::FieldListIterator it = lst.begin();
			 it != end;
			 ++it )
	{
		Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it );
		if ( sf )
		{
			if ( sf->tag() == NM_A_DISPLAY_NAME )
				continue;
			else if ( sf->tag() == NM_A_CHAT_OWNER_DN )
				m_ownerDn = sf->value().toString();
			else if ( sf->tag() == NM_A_CHAT_CREATOR_DN )
				m_creatorDn= sf->value().toString();
			else if ( sf->tag() == NM_A_DESCRIPTION )
				m_description =  sf->value().toString();
			else if ( sf->tag() == NM_A_DISCLAIMER )
				m_disclaimer = sf->value().toString();
			else if ( sf->tag() == NM_A_QUERY )
				m_query = sf->value().toString();
			else if ( sf->tag() == NM_A_ARCHIVE )
				m_archive = sf->value().toString();
			else if ( sf->tag() == NM_A_SZ_TOPIC )
				m_topic = sf->value().toString();
			else if ( sf->tag() == NM_A_CREATION_TIME )
				m_creationTime.setTime_t( sf->value().toInt() );
			else if ( sf->tag() == NM_A_UD_CHAT_RIGHTS )
				m_rights = sf->value().toInt();
			
		}
		else
		{
			Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it );
			if ( mf )
			{
				if ( mf->tag() == NM_A_FA_CHAT_ACL )
				{
					Field::FieldList acl = mf->fields();
					const Field::FieldListIterator aclEnd = acl.end();
					for ( Field::FieldListIterator aclIt = acl.begin();
										 aclIt != aclEnd;
										 ++aclIt )
					{
						Field::MultiField * aclEntryFields = dynamic_cast<Field::MultiField *>( *aclIt );
						if ( aclEntryFields )
						{
							ChatContact entry;
							Field::FieldList entryFields = aclEntryFields->fields();
							Field::SingleField * sf; 
							if ( ( sf = entryFields.findSingleField ( NM_A_SZ_DN ) ) )
								entry.dn = sf->value().toString();
							if ( ( sf = entryFields.findSingleField ( NM_A_SZ_ACCESS_FLAGS ) ) )
								entry.chatRights = sf->value().toInt();
							kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "got acl entry: " << entry.dn << ", " << entry.chatRights << endl;
							m_aclEntries.append( entry );
						}
						
					}
				}
			}
		}
	}
	kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Got chatroom properties: " << m_chat << " : " << m_ownerDn << ", " << m_description << ", " << m_disclaimer << ", " << m_query << ", " << m_archive << ", " << m_topic << ", " << m_creatorDn << ", " << m_creationTime.toString() << ", " << m_rights << endl;
	finished();
	return true;
}
Transfer * ResponseProtocol::parse( const QByteArray & wire, uint & bytes )
{
	m_bytes = 0;
	m_collatingFields.clear();
	//m_din = new QDataStream( wire, IO_ReadOnly );
	QBuffer inBuf( wire );
	inBuf.open( IO_ReadOnly); 
	m_din.setDevice( &inBuf );
	m_din.setByteOrder( QDataStream::LittleEndian );
	
	// check that this begins with a HTTP (is a response)
	Q_UINT32 val;
	m_din >> val;
	m_bytes += sizeof( Q_UINT32 );
	
	Q_ASSERT( qstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) == 0 );
	
	// read rest of HTTP header and look for a 301 redirect. 
	QCString headerFirst;
	if ( !readGroupWiseLine( headerFirst ) )
		return 0;
	// pull out the HTTP return code
	int firstSpace = headerFirst.find( ' ' );
	QString rtnField = headerFirst.mid( firstSpace, headerFirst.find( ' ', firstSpace + 1 ) );
	bool ok = true;
	int rtnCode;
	int packetState = -1;
	rtnCode = rtnField.toInt( &ok );
	debug( "CoreProtocol::readResponse() got HTTP return code " );
	// read rest of header
	QStringList headerRest;
	QCString line;
	while ( line != "\r\n" )
	{
		if ( !readGroupWiseLine( line ) )
		{
			m_din.unsetDevice();
			return 0;
		}
		headerRest.append( line );
		debug( QString( "- read header line - (%1) : %2" ).arg( line.length() ).arg( line.data() ) );
	}
	debug( "ResponseProtocol::readResponse() header finished" );
	// if it's a redirect, set flag
	if ( ok && rtnCode == 301 )
	{	
		debug( "- server redirect " );
		packetState = ServerRedirect;
		m_din.unsetDevice();
		return 0;
	}
	// other header processing ( 500! )
	if ( ok && rtnCode == 500 )
	{
		debug( QString( "- server error %1" ).arg( rtnCode ) );
		packetState = ServerError;
		m_din.unsetDevice();
		return 0;
	}
	if ( ok && rtnCode == 404 )
	{
		debug( QString( "- server error %1" ).arg( rtnCode ) );
		packetState = ServerError;
		m_din.unsetDevice();
		return 0;
	}
	if ( m_din.atEnd() )
	{
		debug( "- no fields" );
		packetState = ProtocolError;
		m_din.unsetDevice();
		return 0;
	}
	
	// read fields
	if ( !readFields( -1 ) )
	{
		m_din.unsetDevice();
		return 0;
	}
	// find transaction id field and create Response object if nonzero
	int tId = 0;
	int resultCode = 0;
	Field::FieldListIterator it;
	Field::FieldListIterator end = m_collatingFields.end();
	it = m_collatingFields.find( NM_A_SZ_TRANSACTION_ID );
	if ( it != end )
	{
		Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it );
		if ( sf )
		{
			tId = sf->value().toInt();
			debug( QString( "ResponseProtocol::readResponse() - transaction ID is %1" ).arg( tId ) );
			m_collatingFields.remove( it );
			delete sf;
		}
	}
	it = m_collatingFields.find( NM_A_SZ_RESULT_CODE );
	if ( it != end )
	{
		Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it );
		if ( sf )
		{
			resultCode = sf->value().toInt();
			debug( QString( "ResponseProtocol::readResponse() - result code is %1" ).arg( resultCode ) );
			m_collatingFields.remove( it );
			delete sf;
		}
	}
	// append to inQueue
	if ( tId )
	{
		debug( QString( "ResponseProtocol::readResponse() - setting state Available, got %1 fields in base array" ).arg(m_collatingFields.count() ) );
		packetState = Available;
		bytes = m_bytes;
		m_din.unsetDevice();
		return new Response( tId, resultCode, m_collatingFields );
	}
	else
	{
		debug( "- WARNING - NO TRANSACTION ID FOUND!" );
		m_state = ProtocolError;
		m_din.unsetDevice();
		m_collatingFields.purge();
		return 0;
	}
}
GroupWise::ContactDetails PollSearchResultsTask::extractUserDetails( Field::FieldList & fields )
{
	ContactDetails cd;
	cd.status = GroupWise::Invalid;
	cd.archive = false;
	// read the supplied fields, set metadata and status.
	Field::SingleField * sf;
	if ( ( sf = fields.findSingleField ( Field::NM_A_SZ_AUTH_ATTRIBUTE ) ) )
		cd.authAttribute = sf->value().toString();
	if ( ( sf = fields.findSingleField ( Field::NM_A_SZ_DN ) ) )
		cd.dn =sf->value().toString().toLower(); // HACK: lowercased DN
	if ( ( sf = fields.findSingleField ( Field::KOPETE_NM_USER_DETAILS_CN ) ) )
		cd.cn = sf->value().toString();
	if ( ( sf = fields.findSingleField ( Field::KOPETE_NM_USER_DETAILS_GIVEN_NAME ) ) )
		cd.givenName = sf->value().toString();
	if ( ( sf = fields.findSingleField ( Field::KOPETE_NM_USER_DETAILS_SURNAME ) ) )
		cd.surname = sf->value().toString();
	if ( ( sf = fields.findSingleField ( Field::KOPETE_NM_USER_DETAILS_FULL_NAME ) ) )
		cd.fullName = sf->value().toString();
	if ( ( sf = fields.findSingleField ( Field::KOPETE_NM_USER_DETAILS_ARCHIVE_FLAG ) ) )
		cd.archive = ( sf->value().toInt() == 1 );
	if ( ( sf = fields.findSingleField ( Field::NM_A_SZ_STATUS ) ) )
		cd.status = sf->value().toInt();
	if ( ( sf = fields.findSingleField ( Field::NM_A_SZ_MESSAGE_BODY ) ) )
		cd.awayMessage = sf->value().toString();
	Field::MultiField * mf;
	QMap< QString, QVariant > propMap;
	if ( ( mf = fields.findMultiField ( Field::NM_A_FA_INFO_DISPLAY_ARRAY ) ) )
	{
		Field::FieldList fl = mf->fields();
		const Field::FieldListIterator end = fl.end();
		for ( Field::FieldListIterator it = fl.begin(); it != end; ++it )
		{
			// assumes each property only present once
			// check in logintask.cpp and if it's a multi field,
			// get the value of this instance, check if it's already in the property map and append if found.
			Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it );
			if ( propField )
			{
				QString propName = propField->tag();
				QString propValue = propField->value().toString();
				propMap.insert( propName, propValue );
			}
			else
			{
				Field::MultiField * propList = dynamic_cast<Field::MultiField*>( *it );
				if ( propList )
				{
					QString parentName = propList->tag();
					Field::FieldList propFields = propList->fields();
					const Field::FieldListIterator end = propFields.end();
					for ( Field::FieldListIterator it = propFields.begin(); it != end; ++it )
					{
						propField = dynamic_cast<Field::SingleField *>( *it );
						if ( propField )
						{
							QString propValue = propField->value().toString();
							QString contents = propMap[ propField->tag() ].toString();
							if ( !contents.isEmpty() )
								contents.append( ", " );
							contents.append( propField->value().toString());
							propMap.insert( propField->tag(), contents );
						}
					}
				}
			}
		}
	}
	if ( !propMap.empty() )
	{
		cd.properties = propMap;
	}
	return cd;
}