1 /****************************************************************************
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
5 ** This file is part of the QxtNetwork module of the Qt eXTension library
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
21 ** <http://libqxt.sourceforge.net> <foundation@libqxt.org>
23 ****************************************************************************/
25 #include "qxtrpcpeer.h"
31 #include <QMetaMethod>
34 class QxtIntrospector: public QObject {
35 // This class MANUALLY implements the necessary parts of QObject.
36 // Do NOT add the Q_OBJECT macro. As this class isn't intended
37 // for direct use, it doesn't offer any sort of useful meta-object.
39 QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal);
41 int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
50 struct QxtRPCConnection {
56 class QxtRPCPeerPrivate : public QxtPrivate<QxtRPCPeer> {
58 QXT_DECLARE_PUBLIC(QxtRPCPeer);
60 void receivePeerSignal(QString fn, QVariant p0 = QVariant(), QVariant p1 = QVariant(), QVariant p2 = QVariant(), QVariant p3 = QVariant(),
61 QVariant p4 = QVariant(), QVariant p5 = QVariant(), QVariant p6 = QVariant(), QVariant p7 = QVariant(), QVariant p8 = QVariant()) const;
62 void receiveClientSignal(quint64 id, QString fn, QVariant p0 = QVariant(), QVariant p1 = QVariant(), QVariant p2 = QVariant(), QVariant p3 = QVariant(),
63 QVariant p4 = QVariant(), QVariant p5 = QVariant(), QVariant p6 = QVariant(), QVariant p7 = QVariant()) const;
65 void processInput(QIODevice* socket, QByteArray& buffer);
67 // Object -> introspector for each signal
68 QMultiHash<QObject*, QxtIntrospector*> attachedSignals;
69 // RPC function -> (object, slot ID)
70 typedef QPair<QObject*, int> MethodID;
71 QHash<QString, QList<MethodID> > attachedSlots;
73 typedef QHash<QObject*, QxtRPCConnection*> ConnHash;
76 QIODevice* m_peer; ///aep: doom
82 QxtRPCPeer::QxtRPCPeer(QObject* parent) : QObject(parent) {
83 QXT_INIT_PRIVATE(QxtRPCPeer);
84 qxt_d().m_rpctype = Peer;
85 qxt_d().m_server = new QTcpServer(this);
86 qxt_d().m_peer = new QTcpSocket(this);
87 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
88 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
89 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
90 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
91 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
92 QObject::connect(qxt_d().m_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
95 QxtRPCPeer::QxtRPCPeer(RPCTypes type, QObject* parent) : QObject(parent) {
96 QXT_INIT_PRIVATE(QxtRPCPeer);
97 qxt_d().m_rpctype = type;
98 qxt_d().m_server = new QTcpServer(this);
99 qxt_d().m_peer = new QTcpSocket(this);
100 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
101 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
102 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
103 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
104 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
105 QObject::connect(qxt_d().m_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
108 QxtRPCPeer::QxtRPCPeer(QIODevice* device, RPCTypes type, QObject* parent) : QObject(parent) {
109 if (!device->isOpen())
111 qWarning("QxtRPCPeer::the device you passed is not open!");
114 QXT_INIT_PRIVATE(QxtRPCPeer);
115 qxt_d().m_rpctype = type;
116 qxt_d().m_server = new QTcpServer(this);
117 qxt_d().m_peer = device;
119 if (qobject_cast<QTcpSocket *>(device)!=0)
121 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
122 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
123 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
124 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
126 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
127 QObject::connect(qxt_d().m_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
130 void QxtRPCPeer::setRPCType(RPCTypes type) {
131 if(qxt_d().m_peer->isOpen () || qxt_d().m_server->isListening()) {
132 qWarning() << "QxtRPCPeer: Cannot change RPC types while connected or listening";
135 qxt_d().m_rpctype = type;
138 QxtRPCPeer::RPCTypes QxtRPCPeer::rpcType() const {
139 return (RPCTypes)(qxt_d().m_rpctype);
142 void QxtRPCPeer::connect(QHostAddress addr, int port) {
143 if(qxt_d().m_rpctype == Server) {
144 qWarning() << "QxtRPCPeer: Cannot connect outward in Server mode";
146 } else if(qxt_d().m_peer->isOpen ()) {
147 qWarning() << "QxtRPCPeer: Already connected";
150 QTcpSocket * sock = qobject_cast<QTcpSocket*>(qxt_d().m_peer);
152 sock->connectToHost(addr, port);
155 bool QxtRPCPeer::listen(QHostAddress iface, int port) {
156 if(qxt_d().m_rpctype == Client) {
157 qWarning() << "QxtRPCPeer: Cannot listen in Client mode";
159 } else if(qxt_d().m_rpctype == Peer && qxt_d().m_peer->isOpen ()) {
160 qWarning() << "QxtRPCPeer: Cannot listen while connected to a peer";
162 } else if(qxt_d().m_server->isListening()) {
163 qWarning() << "QxtRPCPeer: Already listening";
166 return qxt_d().m_server->listen(iface, port);
169 void QxtRPCPeer::disconnectPeer(quint64 id) {
170 if(qxt_d().m_rpctype == Server && id==0) {
171 qWarning() << "QxtRPCPeer: Server mode does not have a peer";
173 } else if(qxt_d().m_rpctype!= Server && id!=0) {
174 qWarning() << "QxtRPCPeer: Must specify a client ID to disconnect";
177 QxtRPCConnection* conn;
179 qxt_d().m_peer->close();
180 } else if((conn = qxt_d().m_clients.take((QObject*)(id)))!= 0) {
181 conn->socket->disconnectFromHost();
182 conn->socket->deleteLater();
185 qWarning() << "QxtRPCPeer: no client with id " << id;
189 void QxtRPCPeer::disconnectAll() {
190 if(qxt_d().m_rpctype!= Server)
193 for(QxtRPCPeerPrivate::ConnHash::const_iterator i = qxt_d().m_clients.constBegin(); i!= qxt_d().m_clients.constEnd(); i++) {
194 (*i)->socket->deleteLater();
197 qxt_d().m_clients.clear();
201 void QxtRPCPeer::stopListening() {
202 if(!qxt_d().m_server->isListening()) {
203 qWarning() << "QxtRPCPeer: Not listening";
206 qxt_d().m_server->close();
209 bool QxtRPCPeer::attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction) {
210 const QMetaObject* meta = sender->metaObject();
211 QByteArray sig(meta->normalizedSignature(signal).mid(1));
212 int methodID = meta->indexOfMethod(sig.constData());
213 if(methodID == -1 || meta->method(methodID).methodType() != QMetaMethod::Signal) {
214 qWarning() << "QxtRPCPeer::attachSignal: No such signal " << signal;
217 QxtIntrospector* spec = new QxtIntrospector(this, sender, signal);
218 if(!rpcFunction.isEmpty()) {
219 spec->rpcFunction = rpcFunction.simplified();
221 spec->rpcFunction = sig;
223 qxt_d().attachedSignals.insertMulti(sender, spec);
227 bool QxtRPCPeer::attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot) {
228 const QMetaObject* meta = recv->metaObject();
229 int methodID = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
230 if(methodID == -1 || meta->method(methodID).methodType() == QMetaMethod::Method) {
231 qWarning() << "QxtRPCPeer::attachSlot: No such slot " << slot;
236 if(rpcFunction[0] == '1' && rpcFunction.contains('(') && rpcFunction.contains(')')) {
237 fn = QMetaObject::normalizedSignature(rpcFunction.mid(1).constData());
239 fn = rpcFunction.simplified();
242 qxt_d().attachedSlots[fn].append(QPair<QObject*, int>(recv, recv->metaObject()->indexOfMethod(recv->metaObject()->normalizedSignature(slot).mid(1))));
246 void QxtRPCPeer::detachSender() {
247 detachObject(sender());
250 void QxtRPCPeer::detachObject(QObject* obj) {
251 foreach(QxtIntrospector* i, qxt_d().attachedSignals.values(obj)) i->deleteLater();
252 qxt_d().attachedSignals.remove(obj);
253 foreach(QString slot, qxt_d().attachedSlots.keys()) {
254 for(QList<QPair<QObject*, int> >::iterator i(qxt_d().attachedSlots[slot].begin());
255 i!= qxt_d().attachedSlots[slot].end(); ) {
256 if((*i).first == obj)
257 i = qxt_d().attachedSlots[slot].erase(i);
264 QByteArray QxtRPCPeer::serialize(QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) const {
266 QDataStream str(&rv, QIODevice::WriteOnly);
268 unsigned char ct = 9;
269 if(p1.isNull()) ct = 0;
270 else if(p2.isNull()) ct = 1;
271 else if(p3.isNull()) ct = 2;
272 else if(p4.isNull()) ct = 3;
273 else if(p5.isNull()) ct = 4;
274 else if(p6.isNull()) ct = 5;
275 else if(p7.isNull()) ct = 6;
276 else if(p8.isNull()) ct = 7;
277 else if(p9.isNull()) ct = 8;
288 rv.replace(QByteArray("\\"), QByteArray("\\\\"));
289 rv.replace(QByteArray("\n"), QByteArray("\\n"));
294 void QxtRPCPeer::call(const char * signal , QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) {
296 QByteArray sig=QMetaObject::normalizedSignature(signal);
298 if(!qxt_d().m_peer->isOpen ())
300 qWarning("can't call on a closed device");
303 qxt_d().m_peer->write(serialize(sig, p1, p2, p3, p4, p5, p6, p7, p8, p9));
306 void QxtRPCPeer::callClientList(QList<quint64> ids, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8) {
307 QByteArray c = serialize(fn, p1, p2, p3, p4, p5, p6, p7, p8, QVariant());
308 foreach(quint64 id, ids) {
309 QxtRPCConnection* conn = qxt_d().m_clients.value((QObject*)(id));
311 qWarning() << "QxtRPCPeer: no client with id" << id;
313 conn->socket->write(c);
318 void QxtRPCPeer::callClient(quint64 id, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8) {
319 callClientList(QList<quint64>() << id, fn, p1, p2, p3, p4, p5, p6, p7, p8);
322 void QxtRPCPeer::callClientsExcept(quint64 id, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8) {
323 QList<quint64> cs = clients();
325 callClientList(cs, fn, p1, p2, p3, p4, p5, p6, p7, p8);
328 #define QXT_ARG(i) ((numParams>i)?QGenericArgument(p ## i .typeName(), p ## i .constData()):QGenericArgument())
329 void QxtRPCPeerPrivate::receivePeerSignal(QString fn, QVariant p0, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8) const {
332 foreach(QxtRPCPeerPrivate::MethodID i, attachedSlots.value(fn)) {
333 sig = i.first->metaObject()->method(i.second).signature();
334 sig = sig.left(sig.indexOf('('));
335 numParams = i.first->metaObject()->method(i.second).parameterTypes().count();
336 QMetaObject::invokeMethod(i.first, sig, QXT_ARG(0), QXT_ARG(1), QXT_ARG(2), QXT_ARG(3), QXT_ARG(4), QXT_ARG(5), QXT_ARG(6), QXT_ARG(7), QXT_ARG(8));
340 void QxtRPCPeerPrivate::receiveClientSignal(quint64 id, QString fn, QVariant p0, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7) const {
343 foreach(QxtRPCPeerPrivate::MethodID i, attachedSlots.value(fn)) {
344 sig = i.first->metaObject()->method(i.second).signature();
345 sig = sig.left(sig.indexOf('('));
346 numParams = i.first->metaObject()->method(i.second).parameterTypes().count();
347 QMetaObject::invokeMethod(i.first, sig, Q_ARG(quint64, id), QXT_ARG(0), QXT_ARG(1), QXT_ARG(2), QXT_ARG(3), QXT_ARG(4), QXT_ARG(5), QXT_ARG(6), QXT_ARG(7));
352 void QxtRPCPeer::newConnection() {
353 QTcpSocket* next = qxt_d().m_server->nextPendingConnection();
354 if(qxt_d().m_rpctype == QxtRPCPeer::Peer) {
355 if(qxt_d().m_peer->isOpen ()) {
356 qWarning() << "QxtRPCPeer: Rejected connection from " << next->peerAddress().toString() << "; another peer is connected";
357 next->disconnectFromHost();
360 qxt_d().m_peer->deleteLater();
361 qxt_d().m_peer = next;
362 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
363 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
364 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
365 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
366 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
367 emit peerConnected();
370 QxtRPCConnection* conn = new QxtRPCConnection;
372 qxt_d().m_clients[next] = conn;
373 QObject::connect(next, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
374 QObject::connect(next, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
375 QObject::connect(next, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
376 emit clientConnected((quint64)(next));
380 void QxtRPCPeer::dataAvailable() {
381 if(qxt_d().m_rpctype!=QxtRPCPeer::Server && qxt_d().m_peer==sender()) {
382 qxt_d().m_buffer.append(qxt_d().m_peer->readAll());
383 qxt_d().processInput(qxt_d().m_peer, qxt_d().m_buffer);
386 QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
388 qWarning() << "QxtRPCPeer: Unrecognized client object connected to dataAvailable";
391 conn->buffer.append(conn->socket->readAll());
392 qxt_d().processInput(conn->socket, (conn->buffer));
395 qWarning() << "QxtRPCPeer: Unrecognized peer object connected to dataAvailable";
398 void QxtRPCPeer::disconnectSender() {
399 QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
401 if(qxt_d().m_peer!= qobject_cast<QTcpSocket*>(sender())) {
402 qWarning() << "QxtRPCPeer: Unrecognized object connected to disconnectSender";
405 qxt_d().m_buffer.append(qxt_d().m_peer->readAll());
406 qxt_d().m_buffer.append("\n");
407 qxt_d().processInput(qxt_d().m_peer, qxt_d().m_buffer);
408 qxt_d().m_buffer.clear();
409 emit clientDisconnected((quint64)(sender()));
412 conn->buffer.append(conn->socket->readAll());
413 conn->buffer.append("\n");
414 qxt_d().processInput(conn->socket, conn->buffer);
415 conn->socket->deleteLater();
417 qxt_d().m_clients.remove(sender());
420 void QxtRPCPeerPrivate::processInput(QIODevice* socket, QByteArray& buffer) {
421 while(qxt_p().canDeserialize(buffer)) {
422 QPair<QString, QList<QVariant> > sig = qxt_p().deserialize(buffer);
423 if(sig.first.isEmpty()) {
424 if(sig.second.count()) {
425 qWarning() << "QxtRPCPeer: Invalid data received; disconnecting";
427 qxt_p().disconnectPeer();
429 qxt_p().disconnectPeer((quint64)(socket));
434 while(sig.second.count() < 9) sig.second << QVariant();
435 if(socket == m_peer) {
436 receivePeerSignal(sig.first, sig.second[0], sig.second[1], sig.second[2], sig.second[3], sig.second[4], sig.second[5], sig.second[6], sig.second[7], sig.second[8]);
438 receiveClientSignal((quint64)(socket), sig.first, sig.second[0], sig.second[1], sig.second[2], sig.second[3], sig.second[4], sig.second[5], sig.second[6], sig.second[7]);
443 QList<quint64> QxtRPCPeer::clients() const {
445 QList<QObject*> cs = qxt_d().m_clients.keys();
446 foreach(QObject* id, cs) rv << (const quint64)(id);
450 QxtIntrospector::QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal): QObject(parent) {
452 QByteArray sig_ba = QMetaObject::normalizedSignature(QByteArray(signal).mid(1));
453 ///FIXME: use normalizedsignature
454 const char * sig=sig_ba.constData();
455 int idx = source->metaObject()->indexOfSignal(sig);
457 qWarning("no such signal: %s",sig_ba.constData());
459 // Our "method" will have the first ID not used by the superclass.
460 QMetaObject::connect(source, idx, this, QObject::staticMetaObject.methodCount());
461 QObject::connect(source, SIGNAL(destroyed()), peer, SLOT(detachSender()));
462 QList<QByteArray> p = source->metaObject()->method(idx).parameterTypes();
464 for(int i=0; i<ct; i++) argTypes.append(QMetaType::type(p.value(i).constData()));
467 int QxtIntrospector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
468 _id = QObject::qt_metacall(_c, _id, _a);
471 if (_c == QMetaObject::InvokeMetaMethod) {
474 int n = argTypes.size();
475 for(int i=0; i<n; i++) v[i] = QVariant(argTypes[i], _a[i+1]);
476 peer->call(rpcFunction.toUtf8().constData(), v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
483 QPair<QString, QList<QVariant> > QxtRPCPeer::deserialize(QByteArray& data) {
485 int pos = data.indexOf('\n');
486 cmd = data.left(pos-1);
487 data = data.mid(pos+1);
488 if(cmd.length()==0) return qMakePair(QString(), QList<QVariant>());
489 cmd.replace(QByteArray("\\n"), QByteArray("\n"));
490 cmd.replace(QByteArray("\\\\"), QByteArray("\\"));
491 QDataStream str(cmd);
493 unsigned char argCount;
496 str >> signal >> argCount;
498 if(str.status() == QDataStream::ReadCorruptData) {
500 return qMakePair(QString(), v);
503 for(int i=0; i<argCount; i++) {
507 return qMakePair(signal, v);
510 bool QxtRPCPeer::canDeserialize(const QByteArray& buffer) const {
511 if (buffer.indexOf('\n') == -1)