|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (C) 2000 Stefan Westerfeld
|
|
|
|
stefan@space.twc.de
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 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
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "connection.h"
|
|
|
|
#include "dispatcher.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h> // for Solaris
|
|
|
|
#include <queue>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
using namespace Arts;
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
namespace Arts {
|
|
|
|
class ConnectionPrivate {
|
|
|
|
public:
|
|
|
|
struct Data {
|
|
|
|
Data() : data(0), len(0) { }
|
|
|
|
Data(unsigned char *data, long len) : data(data), len(len) { }
|
|
|
|
Data(const Data& d) : data(d.data), len(d.len) { }
|
|
|
|
unsigned char *data;
|
|
|
|
long len;
|
|
|
|
};
|
|
|
|
|
|
|
|
queue<Data> incoming;
|
|
|
|
map<string,string> hints;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Connection::Connection() :d(new ConnectionPrivate), _refCnt(1)
|
|
|
|
{
|
|
|
|
_connState = unknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
Connection::~Connection()
|
|
|
|
{
|
|
|
|
assert(d->incoming.empty());
|
|
|
|
assert(_refCnt == 0);
|
|
|
|
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::_copy()
|
|
|
|
{
|
|
|
|
assert(_refCnt > 0);
|
|
|
|
_refCnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::_release()
|
|
|
|
{
|
|
|
|
assert(_refCnt > 0);
|
|
|
|
_refCnt--;
|
|
|
|
if(_refCnt == 0)
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::initReceive()
|
|
|
|
{
|
|
|
|
rcbuf = 0;
|
|
|
|
receiveHeader = true;
|
|
|
|
remaining = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::receive(unsigned char *newdata, long newlen)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* protect against being freed while receive is running, as there are a
|
|
|
|
* few points where reentrant event loops may happen (Dispatcher::handle)
|
|
|
|
*/
|
|
|
|
_copy();
|
|
|
|
|
|
|
|
d->incoming.push(ConnectionPrivate::Data(newdata,newlen));
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ConnectionPrivate::Data &data = d->incoming.front();
|
|
|
|
|
|
|
|
// get a buffer for the incoming message
|
|
|
|
if(!rcbuf) rcbuf = new Buffer;
|
|
|
|
|
|
|
|
// put a suitable amount of input data into the receive buffer
|
|
|
|
long len = min(remaining, data.len);
|
|
|
|
|
|
|
|
remaining -= len;
|
|
|
|
rcbuf->write(data.data,len);
|
|
|
|
|
|
|
|
data.len -= len;
|
|
|
|
data.data += len;
|
|
|
|
|
|
|
|
if(data.len == 0)
|
|
|
|
d->incoming.pop();
|
|
|
|
|
|
|
|
// look if it was enough to do something useful with it
|
|
|
|
if(remaining == 0)
|
|
|
|
{
|
|
|
|
if(receiveHeader)
|
|
|
|
{
|
|
|
|
long mcopMagic;
|
|
|
|
|
|
|
|
mcopMagic = rcbuf->readLong();
|
|
|
|
remaining = rcbuf->readLong() - 12;
|
|
|
|
messageType = rcbuf->readLong();
|
|
|
|
|
|
|
|
if(_connState != Connection::established
|
|
|
|
&& (remaining >= 4096 || remaining < 0))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* don't accept large amounts of data on unauthenticated
|
|
|
|
* connections
|
|
|
|
*/
|
|
|
|
remaining = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mcopMagic == MCOP_MAGIC)
|
|
|
|
{
|
|
|
|
// do we need to receive more data (message body?)
|
|
|
|
if(remaining)
|
|
|
|
{
|
|
|
|
receiveHeader = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Buffer *received = rcbuf;
|
|
|
|
initReceive();
|
|
|
|
Dispatcher::the()->handle(this,received,messageType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
initReceive();
|
|
|
|
Dispatcher::the()->handleCorrupt(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Buffer *received = rcbuf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* it's important to prepare to receive new messages *before*
|
|
|
|
* calling Dispatcher::the()->handle(...), as handle may
|
|
|
|
* get into an I/O situation (e.g. when doing an invocation
|
|
|
|
* itself), and we may receive more messages while handle is
|
|
|
|
* running
|
|
|
|
*/
|
|
|
|
initReceive();
|
|
|
|
|
|
|
|
// rcbuf is consumed by the dispatcher
|
|
|
|
Dispatcher::the()->handle(this,received,messageType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while(!d->incoming.empty());
|
|
|
|
|
|
|
|
_release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Connection::setHints(const vector<string>& hints)
|
|
|
|
{
|
|
|
|
vector<string>::const_iterator i;
|
|
|
|
|
|
|
|
for(i = hints.begin(); i != hints.end(); i++)
|
|
|
|
{
|
|
|
|
string key;
|
|
|
|
vector<string> values;
|
|
|
|
|
|
|
|
if(MCOPUtils::tokenize(*i, key, values))
|
|
|
|
{
|
|
|
|
if(values.size() == 1)
|
|
|
|
d->hints[key] = values[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string Connection::findHint(const string& hint)
|
|
|
|
{
|
|
|
|
return d->hints[hint];
|
|
|
|
}
|