|
|
|
/*
|
|
|
|
Kopete Groupwise Protocol
|
|
|
|
responseprotocol.cpp - Protocol used for reading incoming GroupWise Responses
|
|
|
|
|
|
|
|
Copyright (c) 2004 SUSE Linux AG http://www.suse.com
|
|
|
|
|
|
|
|
Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
|
|
|
|
|
|
|
|
*************************************************************************
|
|
|
|
* *
|
|
|
|
* 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 of the License, or (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
*************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <tqbuffer.h>
|
|
|
|
|
|
|
|
#include "response.h"
|
|
|
|
|
|
|
|
#include "responseprotocol.h"
|
|
|
|
|
|
|
|
ResponseProtocol::ResponseProtocol(TQObject* parent, const char* name): InputProtocolBase(parent, name)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ResponseProtocol::~ResponseProtocol()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Transfer * ResponseProtocol::parse( const TQByteArray & wire, uint & bytes )
|
|
|
|
{
|
|
|
|
m_bytes = 0;
|
|
|
|
m_collatingFields.clear();
|
|
|
|
//m_din = new TQDataStream( wire, IO_ReadOnly );
|
|
|
|
TQBuffer inBuf( wire );
|
|
|
|
inBuf.open( IO_ReadOnly);
|
|
|
|
m_din.setDevice( &inBuf );
|
|
|
|
m_din.setByteOrder( TQDataStream::LittleEndian );
|
|
|
|
|
|
|
|
// check that this begins with a HTTP (is a response)
|
|
|
|
TQ_UINT32 val;
|
|
|
|
m_din >> val;
|
|
|
|
m_bytes += sizeof( TQ_UINT32 );
|
|
|
|
|
|
|
|
Q_ASSERT( tqstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) == 0 );
|
|
|
|
|
|
|
|
// read rest of HTTP header and look for a 301 redirect.
|
|
|
|
TQCString headerFirst;
|
|
|
|
if ( !readGroupWiseLine( headerFirst ) )
|
|
|
|
return 0;
|
|
|
|
// pull out the HTTP return code
|
|
|
|
int firstSpace = headerFirst.find( ' ' );
|
|
|
|
TQString 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
|
|
|
|
TQStringList headerRest;
|
|
|
|
TQCString line;
|
|
|
|
while ( line != "\r\n" )
|
|
|
|
{
|
|
|
|
if ( !readGroupWiseLine( line ) )
|
|
|
|
{
|
|
|
|
m_din.unsetDevice();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
headerRest.append( line );
|
|
|
|
debug( TQString( "- 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( TQString( "- server error %1" ).arg( rtnCode ) );
|
|
|
|
packetState = ServerError;
|
|
|
|
m_din.unsetDevice();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ( ok && rtnCode == 404 )
|
|
|
|
{
|
|
|
|
debug( TQString( "- 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( TQString( "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( TQString( "ResponseProtocol::readResponse() - result code is %1" ).arg( resultCode ) );
|
|
|
|
m_collatingFields.remove( it );
|
|
|
|
delete sf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// append to inQueue
|
|
|
|
if ( tId )
|
|
|
|
{
|
|
|
|
debug( TQString( "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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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( TQString( "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
|
|
|
|
TQ_UINT8 type, method;
|
|
|
|
TQ_UINT32 val;
|
|
|
|
TQCString tag;
|
|
|
|
// read uint8 type
|
|
|
|
if ( !okToProceed() )
|
|
|
|
{
|
|
|
|
currentList.purge();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_din >> type;
|
|
|
|
m_bytes += sizeof( TQ_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( TQ_UINT8 );
|
|
|
|
// read tag and length
|
|
|
|
if ( !safeReadBytes( tag, val ) )
|
|
|
|
{
|
|
|
|
currentList.purge();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug( TQString( "- 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( TQ_UINT32 );
|
|
|
|
|
|
|
|
// create multifield
|
|
|
|
debug( TQString( " multi field containing: %1" ).arg( val ) );
|
|
|
|
Field::MultiField* m = new Field::MultiField( tag, method, 0, type );
|
|
|
|
currentList.append( m );
|
|
|
|
if ( !readFields( val, ¤tList) )
|
|
|
|
{
|
|
|
|
currentList.purge();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
if ( type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN )
|
|
|
|
{
|
|
|
|
TQCString 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 TQt<3.3.2 doesn't sanity check val.
|
|
|
|
TQString fieldValue = TQString::fromUtf8( rawData.data(), val - 1 );
|
|
|
|
debug( TQString( "- 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( TQ_UINT32 );
|
|
|
|
debug( TQString( "- 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--;
|
|
|
|
}
|
|
|
|
// got a whole list!
|
|
|
|
// if fieldCount == 0, we've just read a whole nested list, so add this list to the last element in 'list'
|
|
|
|
if ( fieldCount == 0 && list )
|
|
|
|
{
|
|
|
|
debug( "- finished reading nested list" );
|
|
|
|
Field::MultiField * m = dynamic_cast<Field::MultiField*>( list->last() );
|
|
|
|
m->setFields( currentList );
|
|
|
|
}
|
|
|
|
|
|
|
|
// if fieldCount == -1; we're done reading the top level fieldlist, so store it.
|
|
|
|
if ( fieldCount == -1 )
|
|
|
|
{
|
|
|
|
debug( "- finished reading ALL FIELDS!" );
|
|
|
|
m_collatingFields = currentList;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ResponseProtocol::readGroupWiseLine( TQCString & line )
|
|
|
|
{
|
|
|
|
line = TQCString();
|
|
|
|
while ( true )
|
|
|
|
{
|
|
|
|
TQ_UINT8 c;
|
|
|
|
|
|
|
|
if (! okToProceed() )
|
|
|
|
return false;
|
|
|
|
m_din >> c;
|
|
|
|
m_bytes++;
|
|
|
|
line += TQChar(c).latin1();
|
|
|
|
if ( c == '\n' )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "responseprotocol.moc"
|