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 ChatPropertiesTask::setChat( const QString &displayName )
{
	Field::FieldList lst;
	m_chat = displayName;
	lst.append( new Field::SingleField( NM_A_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_chat ) );
	createTransfer( "chatproperties", lst );
}
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;
}
void GetStatusTask::userDN( const QString & dn )
{
	m_userDN = dn;
	// set up Transfer
	Field::FieldList lst;
	// changed from USERID to DN as per Gaim/GWIM
	lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, m_userDN ) );
	createTransfer( "getstatus", lst );
}
void CreateContactInstanceTask::contact( Field::SingleField * id, const QString & displayName, const int parentFolder )
{
	Field::FieldList lst;
	lst.append( new Field::SingleField( Field::NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentFolder ) ) );
	// this is either a user Id or a DN
	lst.append( id );
	if ( displayName.isEmpty() ) // fallback so that the contact is created
		lst.append( new Field::SingleField( Field::NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_dn ) );
	else
		lst.append( new Field::SingleField( Field::NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, displayName ) );
	createTransfer( "createcontact", lst );
}
int main()
{
    CoreProtocol testObject;
    RequestFactory testFactory;
    QByteArray command("login");
    Request * firstRequest = testFactory.request( command );
    Field::FieldList lst;
    lst.append( new Field::SingleField( Field::NM_A_SZ_USERID, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_UTF8, "*****@*****.**" ) );
    firstRequest->setFields( lst );
    testObject.outgoingTransfer( firstRequest );
    return 0;
}
void UpdateFolderTask::renameFolder( const QString & newName, const GroupWise::FolderItem & existing )
{
	Field::FieldList lst;
	// add the old version of the folder, marked delete
	lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, folderToFields( existing) ) );
	
	GroupWise::FolderItem renamed = existing;
	renamed.name = newName;
	// add the new version of the folder, marked add
	lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_ARRAY, folderToFields( renamed ) ) );
	// let our parent class package it up as a contactlist in a transfer
	UpdateItemTask::item( lst );
}
bool ModifyContactListTask::take( Transfer * transfer )
{
	if ( !forMe( transfer ) )
		return false;
	Response * response = dynamic_cast<Response *>( transfer );
	if ( !response )
		return false;
	client()->debug( "ModifyContactListTask::take()" );

	// scan the contact list received
	// emit each add and delete as a signal
	Field::FieldList fl = response->fields();
	fl.dump( true );
	Field::FieldListIterator it = fl.begin();
	Field::FieldListIterator end = fl.end();
	Field::MultiField * current = fl.findMultiField( NM_A_FA_RESULTS );
	if ( current )
		fl = current->fields();
	current = fl.findMultiField( NM_A_FA_CONTACT_LIST );
	if ( current )
	{
		Field::FieldList contactList = current->fields();
		Field::FieldListIterator cursor = contactList.begin();
		const Field::FieldListIterator end = contactList.end();
		while ( cursor != end )
		{
			Field::MultiField * mf = dynamic_cast< Field::MultiField * >( *cursor );
			if ( mf->tag() == NM_A_FA_CONTACT )
			{
				// contact change
				processContactChange( mf );
			}
			else if ( mf->tag() == NM_A_FA_FOLDER )
			{
				// folder change
				processFolderChange( mf );
			}
			++cursor;
		}
	}
	// TODO: call virtual here to read any fields after the contact list...
	if ( response->resultCode() == GroupWise::None )
		setSuccess();
	else 
		setError( response->resultCode() );
	return true;
}
void SetStatusTask::status( Status newStatus, const QString &awayMessage, const QString &autoReply )
{
	if ( newStatus > GroupWise::Invalid )
	{
		setError( 1, "Invalid Status" );
		return;
	}
	
	m_status = newStatus;
	m_awayMessage = awayMessage;
	m_autoReply = autoReply;
	
	Field::FieldList lst;
	lst.append( new Field::SingleField( NM_A_SZ_STATUS, 0, NMFIELD_TYPE_UTF8, QString::number( newStatus ) ) );
	if ( !awayMessage.isNull() )
		lst.append( new Field::SingleField( NM_A_SZ_STATUS_TEXT, 0, NMFIELD_TYPE_UTF8, awayMessage ) );
	if ( !autoReply.isNull() )
		lst.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, autoReply ) );
	createTransfer( "setstatus", lst );
}
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 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;
}
Field::FieldList UpdateFolderTask::folderToFields( const GroupWise::FolderItem & folder )
{
	Field::FieldList lst;
	lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, folder.id ) );
	lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, 0 ) );
	lst.append( new Field::SingleField( NM_A_SZ_TYPE, 0, NMFIELD_TYPE_UTF8, 1 ) );
	lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, folder.sequence ) );
	if ( !folder.name.isEmpty() )
		lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, folder.name ) );
	return lst;
}
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;
}
bool ResponseProtocol::readFields( int fieldCount, Field::FieldList * list )
{
	// build a list of fields.  
	// If there is already a list of fields stored in m_collatingFields, 
	// the list we're reading on this iteration must be a nested list
	// so when we're done reading it, add it to the MultiList element
	// that is the last element in the top list in m_collatingFields.
	// if we find the beginning of a new nested list, push the current list onto m_collatingFields
	debug( "ResponseProtocol::readFields()" );
	if ( fieldCount > 0 )
		debug( QString( "reading %1 fields" ).arg( fieldCount ) );
	Field::FieldList currentList;
	while ( fieldCount != 0 )  // prevents bad input data from ruining our day
	{
		// the field being read
		// read field
		Q_UINT8 type, method;
		Q_UINT32 val;
		QCString tag;
		// read uint8 type
		if ( !okToProceed() )
		{
			currentList.purge();
			return false;
		}
		m_din >> type;
		m_bytes += sizeof( Q_UINT8 );
		// if type is 0 SOMETHING_INVALID, we're at the end of the fields
		if ( type == 0 ) /*&& m_din->atEnd() )*/
		{
			debug( "- end of field list" );
			m_packetState = FieldsRead;
			// do something to indicate we're done
			break;
		}
		// read uint8 method
		if ( !okToProceed() )
		{
			currentList.purge();
			return false;
		}
		m_din >> method;
		m_bytes += sizeof( Q_UINT8 );
		// read tag and length
		if ( !safeReadBytes( tag, val ) )
		{
			currentList.purge();
			return false;
		}

		debug( QString( "- type: %1, method: %2, tag: %3," ).arg( type ).arg( method ).arg( tag.data() ) );
		// if multivalue or array
		if ( type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY )
		{
			// read length uint32
			if ( !okToProceed() )
			{
				currentList.purge();
				return false;
			}
			m_din >> val;
			m_bytes += sizeof( Q_UINT32 );

			// create multifield
			debug( QString( " multi field containing: %1" ).arg( val ) );
			Field::MultiField* m = new Field::MultiField( tag, method, 0, type );
			currentList.append( m );
			if ( !readFields( val, &currentList) )
			{
				currentList.purge();
				return false;
			}
		}
		else 
		{
		
			if ( type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN )
			{
				QCString rawData;
				if( !safeReadBytes( rawData, val ) )
				{
					currentList.purge();
					return false;
				}
				if ( val > NMFIELD_MAX_STR_LENGTH )
				{
					m_packetState = ProtocolError;
					break;
				}
				// convert to unicode - ignore the terminating NUL, because Qt<3.3.2 doesn't sanity check val.
				QString fieldValue = QString::fromUtf8( rawData.data(), val - 1 );
				debug( QString( "- utf/dn single field: %1" ).arg( fieldValue ) );
				// create singlefield
				Field::SingleField* s = new Field::SingleField( tag, method, 0, type, fieldValue );
				currentList.append( s );
			}
			else
			{
				// otherwise ( numeric )
				// read value uint32
				if ( !okToProceed() )
				{
					currentList.purge();
					return false;
				}
				m_din >> val;
				m_bytes += sizeof( Q_UINT32 );
				debug( QString( "- numeric field: %1" ).arg( val ) );
				Field::SingleField* s = new Field::SingleField( tag, method, 0, type, val );
				currentList.append( s );
			}
		}
		// decrease the fieldCount if we're using it
		if ( fieldCount > 0 )
			fieldCount--;
	}
void PollSearchResultsTask::poll( const QString & queryHandle )
{
	Field::FieldList lst;
	lst.append( new Field::SingleField( Field::NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, queryHandle ) );
	createTransfer( "getresults", lst );
}
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;
}