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>
33 #include "qxtmetaobject.h"
36 class QxtIntrospector: public QObject
38 // This class MANUALLY implements the necessary parts of QObject.
39 // Do NOT add the Q_OBJECT macro. As this class isn't intended
40 // for direct use, it doesn't offer any sort of useful meta-object.
42 QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal);
44 int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
53 struct QxtRPCConnection
60 class QxtRPCPeerPrivate : public QxtPrivate<QxtRPCPeer>, public QTcpServer
63 QXT_DECLARE_PUBLIC(QxtRPCPeer);
65 void incomingConnection ( int socketDescriptor );
68 void receivePeerSignal(QString fn, QVariant p0 = QVariant(), QVariant p1 = QVariant(), QVariant p2 = QVariant(), QVariant p3 = QVariant(),
69 QVariant p4 = QVariant(), QVariant p5 = QVariant(), QVariant p6 = QVariant(), QVariant p7 = QVariant(), QVariant p8 = QVariant()) const;
70 void receiveClientSignal(quint64 id, QString fn, QVariant p0 = QVariant(), QVariant p1 = QVariant(), QVariant p2 = QVariant(), QVariant p3 = QVariant(),
71 QVariant p4 = QVariant(), QVariant p5 = QVariant(), QVariant p6 = QVariant(), QVariant p7 = QVariant()) const;
73 void processInput(QIODevice* socket, QByteArray& buffer);
75 // Object -> introspector for each signal
76 QMultiHash<QObject*, QxtIntrospector*> attachedSignals;
77 // RPC function -> (object, slot ID)
78 typedef QPair<QObject*, int> MethodID;
79 QHash<QString, QList<MethodID> > attachedSlots;
81 typedef QHash<QObject*, QxtRPCConnection*> ConnHash;
89 QStack<QTcpSocket*> pending_connections;
93 QxtRPCPeer::QxtRPCPeer(QObject* parent) : QObject(parent)
95 QXT_INIT_PRIVATE(QxtRPCPeer);
96 qxt_d().m_rpctype = Peer;
97 qxt_d().m_peer = new QTcpSocket(this);
98 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
99 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
100 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
101 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
102 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
106 QxtRPCPeer::QxtRPCPeer(RPCTypes type, QObject* parent) : QObject(parent)
108 QXT_INIT_PRIVATE(QxtRPCPeer);
109 qxt_d().m_rpctype = type;
110 qxt_d().m_peer = new QTcpSocket(this);
111 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
112 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
113 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
114 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
115 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
119 QxtRPCPeer::QxtRPCPeer(QIODevice* device, RPCTypes type, QObject* parent) : QObject(parent)
121 if (!device->isOpen())
123 qWarning("QxtRPCPeer::the device you passed is not open!");
126 QXT_INIT_PRIVATE(QxtRPCPeer);
127 qxt_d().m_rpctype = type;
128 qxt_d().m_peer = device;
130 if (qobject_cast<QAbstractSocket *>(device)!=0)
132 QObject::connect(qxt_d().m_peer, SIGNAL(connected()), this, SIGNAL(peerConnected()));
133 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SIGNAL(peerDisconnected()));
134 QObject::connect(qxt_d().m_peer, SIGNAL(disconnected()), this, SLOT(disconnectSender()));
135 QObject::connect(qxt_d().m_peer, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(peerError(QAbstractSocket::SocketError)));
137 QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
141 void QxtRPCPeer::setRPCType(RPCTypes type)
143 if (qxt_d().m_peer->isOpen () || qxt_d().isListening())
145 qWarning() << "QxtRPCPeer: Cannot change RPC types while connected or listening";
148 qxt_d().m_rpctype = type;
152 QxtRPCPeer::RPCTypes QxtRPCPeer::rpcType() const
154 return (RPCTypes)(qxt_d().m_rpctype);
158 void QxtRPCPeer::connect(QHostAddress addr, int port)
160 if (qxt_d().m_rpctype == Server)
162 qWarning() << "QxtRPCPeer: Cannot connect outward in Server mode";
166 QAbstractSocket * sock = qobject_cast<QAbstractSocket*>(qxt_d().m_peer);
169 qWarning("QxtRPCPeer: cannot connect a custom QIODevice");
173 if (sock->state()!=QAbstractSocket::UnconnectedState)
175 qWarning("QxtRPCPeer: Already connected");
179 sock->connectToHost(addr, port);
183 bool QxtRPCPeer::listen(QHostAddress iface, int port)
185 if (qxt_d().m_rpctype == Client)
187 qWarning() << "QxtRPCPeer: Cannot listen in Client mode";
190 else if (qxt_d().m_rpctype == Peer && qxt_d().m_peer->isOpen ())
192 qWarning() << "QxtRPCPeer: Cannot listen while connected to a peer";
195 else if (qxt_d().isListening())
197 qWarning() << "QxtRPCPeer: Already listening";
200 return qxt_d().listen(iface, port);
204 void QxtRPCPeer::disconnectPeer(quint64 id)
206 if (qxt_d().m_rpctype == Server && id==(quint64)-1)
208 qWarning() << "QxtRPCPeer: Server mode does not have a peer";
211 else if (qxt_d().m_rpctype!= Server && id!=(quint64)-1)
213 qWarning() << "QxtRPCPeer: Must specify a client ID to disconnect";
216 QxtRPCConnection* conn;
219 qxt_d().m_peer->close();
220 ///hackaround for qt bug
221 QAbstractSocket *s =qobject_cast<QAbstractSocket*>( qxt_d().m_peer);
223 s->disconnectFromHost();
226 else if ((conn = qxt_d().m_clients.take((QObject*)(id)))!= 0)
228 conn->socket->disconnectFromHost();
229 conn->socket->deleteLater();
234 qWarning() << "QxtRPCPeer: no client with id " << id;
239 void QxtRPCPeer::disconnectAll()
241 if (qxt_d().m_rpctype!= Server)
245 for (QxtRPCPeerPrivate::ConnHash::const_iterator i = qxt_d().m_clients.constBegin(); i!= qxt_d().m_clients.constEnd(); i++)
247 (*i)->socket->deleteLater();
250 qxt_d().m_clients.clear();
255 void QxtRPCPeer::stopListening()
257 if (!qxt_d().isListening())
259 qWarning() << "QxtRPCPeer: Not listening";
266 bool QxtRPCPeer::attachSignal(QObject* sender, const char* signal, const QByteArray& rpcFunction)
268 const QMetaObject* meta = sender->metaObject();
269 QByteArray sig(meta->normalizedSignature(signal).mid(1));
270 int methodID = meta->indexOfMethod(sig.constData());
271 if (methodID == -1 || meta->method(methodID).methodType() != QMetaMethod::Signal)
273 qWarning() << "QxtRPCPeer::attachSignal: No such signal " << signal;
278 QxtIntrospector* spec = new QxtIntrospector(this, sender, signal);
279 if (!rpcFunction.isEmpty())
281 if (QxtMetaObject::isSignalOrSlot(rpcFunction.constData()))
283 spec->rpcFunction = QMetaObject::normalizedSignature(rpcFunction);
287 spec->rpcFunction = rpcFunction.simplified();
292 spec->rpcFunction = QMetaObject::normalizedSignature(signal);
294 qxt_d().attachedSignals.insertMulti(sender, spec);
299 bool QxtRPCPeer::attachSlot(const QByteArray& rpcFunction, QObject* recv, const char* slot)
301 const QMetaObject* meta = recv->metaObject();
302 int methodID = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
303 if (methodID == -1 || meta->method(methodID).methodType() == QMetaMethod::Method)
305 qWarning() << "QxtRPCPeer::attachSlot: No such slot " << slot;
311 if (QxtMetaObject::isSignalOrSlot(rpcFunction.constData()))
313 fn = QMetaObject::normalizedSignature(rpcFunction.constData());
317 fn = rpcFunction.simplified();
320 qxt_d().attachedSlots[fn].append(QPair<QObject*, int>(recv, recv->metaObject()->indexOfMethod(recv->metaObject()->normalizedSignature(slot).mid(1))));
325 void QxtRPCPeer::detachSender()
327 detachObject(sender());
331 void QxtRPCPeer::detachObject(QObject* obj)
333 foreach(QxtIntrospector* i, qxt_d().attachedSignals.values(obj)) i->deleteLater();
334 qxt_d().attachedSignals.remove(obj);
335 foreach(QString slot, qxt_d().attachedSlots.keys())
337 for (QList<QPair<QObject*, int> >::iterator i(qxt_d().attachedSlots[slot].begin());
338 i!= qxt_d().attachedSlots[slot].end(); )
340 if ((*i).first == obj)
341 i = qxt_d().attachedSlots[slot].erase(i);
349 QByteArray QxtRPCPeer::serialize(QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9) const
352 QDataStream str(&rv, QIODevice::WriteOnly);
354 unsigned char ct = 9;
355 if (p1.isNull()) ct = 0;
356 else if (p2.isNull()) ct = 1;
357 else if (p3.isNull()) ct = 2;
358 else if (p4.isNull()) ct = 3;
359 else if (p5.isNull()) ct = 4;
360 else if (p6.isNull()) ct = 5;
361 else if (p7.isNull()) ct = 6;
362 else if (p8.isNull()) ct = 7;
363 else if (p9.isNull()) ct = 8;
365 if (ct-- >0 ) str << p1;
366 if (ct-- >0) str << p2;
367 if (ct-- >0) str << p3;
368 if (ct-- >0) str << p4;
369 if (ct-- >0) str << p5;
370 if (ct-- >0) str << p6;
371 if (ct-- >0) str << p7;
372 if (ct-- >0) str << p8;
373 if (ct-- >0) str << p9;
374 rv.replace(QByteArray("\\"), QByteArray("\\\\"));
375 rv.replace(QByteArray("\n"), QByteArray("\\n"));
381 void QxtRPCPeer::call(const char * signal , QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8, QVariant p9)
384 QByteArray sig=QMetaObject::normalizedSignature(signal);
386 QAbstractSocket * sock = qobject_cast<QAbstractSocket*>(qxt_d().m_peer);
387 if (!qxt_d().m_peer->isOpen () || ( sock && sock->state()!=QAbstractSocket::ConnectedState ))
389 qWarning("can't call on a closed device");
392 qxt_d().m_peer->write(serialize(sig, p1, p2, p3, p4, p5, p6, p7, p8, p9));
396 void QxtRPCPeer::callClientList(QList<quint64> ids, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8)
398 QByteArray c = serialize(fn, p1, p2, p3, p4, p5, p6, p7, p8, QVariant());
399 foreach(quint64 id, ids)
401 QxtRPCConnection* conn = qxt_d().m_clients.value((QObject*)(id));
404 qWarning() << "QxtRPCPeer: no client with id" << id;
408 conn->socket->write(c);
414 void QxtRPCPeer::callClient(quint64 id, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8)
416 callClientList(QList<quint64>() << id, fn, p1, p2, p3, p4, p5, p6, p7, p8);
420 void QxtRPCPeer::callClientsExcept(quint64 id, QString fn, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8)
422 QList<quint64> cs = clients();
424 callClientList(cs, fn, p1, p2, p3, p4, p5, p6, p7, p8);
428 #define QXT_ARG(i) ((numParams>i)?QGenericArgument(p ## i .typeName(), p ## i .constData()):QGenericArgument())
429 void QxtRPCPeerPrivate::receivePeerSignal(QString fn, QVariant p0, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7, QVariant p8) const
433 foreach(QxtRPCPeerPrivate::MethodID i, attachedSlots.value(fn))
435 sig = i.first->metaObject()->method(i.second).signature();
436 sig = sig.left(sig.indexOf('('));
437 numParams = i.first->metaObject()->method(i.second).parameterTypes().count();
438 if(!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)))
440 qWarning("QxtRPCPeerPrivate::receivePeerSignal: invokeMethod for \"%s\" failed ",sig.constData());
446 void QxtRPCPeerPrivate::receiveClientSignal(quint64 id, QString fn, QVariant p0, QVariant p1, QVariant p2, QVariant p3, QVariant p4, QVariant p5, QVariant p6, QVariant p7) const
450 foreach(QxtRPCPeerPrivate::MethodID i, attachedSlots.value(fn))
452 sig = i.first->metaObject()->method(i.second).signature();
453 sig = sig.left(sig.indexOf('('));
454 numParams = i.first->metaObject()->method(i.second).parameterTypes().count();
455 if(!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)))
457 qWarning("QxtRPCPeerPrivate::receiveClientSignal: invokeMethod for \"%s\" failed ",sig.constData());
465 void QxtRPCPeerPrivate::incomingConnection ( int socketDescriptor )
467 QTcpSocket* next = qxt_p().incomingConnection(socketDescriptor);
468 if (m_rpctype == QxtRPCPeer::Peer)
470 if (m_peer->isOpen ())
472 qWarning() << "QxtRPCPeer: Rejected connection from " << next->peerAddress().toString() << "; another peer is connected";
473 next->disconnectFromHost();
478 m_peer->deleteLater();
480 QObject::connect(m_peer, SIGNAL(connected()), &qxt_p(), SIGNAL(peerConnected()));
481 QObject::connect(m_peer, SIGNAL(disconnected()), &qxt_p(), SIGNAL(peerDisconnected()));
482 QObject::connect(m_peer, SIGNAL(disconnected()), &qxt_p(), SLOT(disconnectSender()));
483 QObject::connect(m_peer, SIGNAL(readyRead()), &qxt_p(), SLOT(dataAvailable()));
484 QObject::connect(m_peer, SIGNAL(error(QAbstractSocket::SocketError)), &qxt_p(), SIGNAL(peerError(QAbstractSocket::SocketError)));
485 emit qxt_p().peerConnected();
490 QxtRPCConnection* conn = new QxtRPCConnection;
492 m_clients[next] = conn;
493 QObject::connect(next, SIGNAL(disconnected()), &qxt_p(), SLOT(disconnectSender()));
494 QObject::connect(next, SIGNAL(readyRead()), &qxt_p(), SLOT(dataAvailable()));
495 QObject::connect(next, SIGNAL(error(QAbstractSocket::SocketError)), &qxt_p(), SIGNAL(peerError(QAbstractSocket::SocketError)));
496 emit qxt_p().clientConnected((quint64)(next));
501 void QxtRPCPeer::dataAvailable()
503 if (qxt_d().m_rpctype!=QxtRPCPeer::Server && qxt_d().m_peer==sender())
505 qxt_d().m_buffer.append(qxt_d().m_peer->readAll());
506 qxt_d().processInput(qxt_d().m_peer, qxt_d().m_buffer);
511 QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
514 qWarning() << "QxtRPCPeer: Unrecognized client object connected to dataAvailable";
517 conn->buffer.append(conn->socket->readAll());
518 qxt_d().processInput(conn->socket, (conn->buffer));
521 qWarning() << "QxtRPCPeer: Unrecognized peer object connected to dataAvailable";
525 void QxtRPCPeer::disconnectSender()
527 QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
530 if (qxt_d().m_peer!= qobject_cast<QIODevice*>(sender()))
532 qWarning() << "QxtRPCPeer: Unrecognized object connected to disconnectSender";
535 qxt_d().m_buffer.append(qxt_d().m_peer->readAll());
536 qxt_d().m_buffer.append("\n");
537 qxt_d().processInput(qxt_d().m_peer, qxt_d().m_buffer);
538 qxt_d().m_buffer.clear();
539 emit clientDisconnected((quint64)(sender()));
542 conn->buffer.append(conn->socket->readAll());
543 conn->buffer.append("\n");
544 qxt_d().processInput(conn->socket, conn->buffer);
545 conn->socket->deleteLater();
547 qxt_d().m_clients.remove(sender());
551 void QxtRPCPeerPrivate::processInput(QIODevice* socket, QByteArray& buffer)
553 while (qxt_p().canDeserialize(buffer))
555 QPair<QString, QList<QVariant> > sig = qxt_p().deserialize(buffer);
556 if (sig.first.isEmpty())
558 if (sig.second.count())
560 qWarning() << "QxtRPCPeer: Invalid data received; disconnecting";
561 if (socket == m_peer)
562 qxt_p().disconnectPeer();
564 qxt_p().disconnectPeer((quint64)(socket));
569 while (sig.second.count() < 9) sig.second << QVariant();
570 if (socket == m_peer)
572 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]);
576 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]);
582 QList<quint64> QxtRPCPeer::clients() const
585 QList<QObject*> cs = qxt_d().m_clients.keys();
586 foreach(QObject* id, cs) rv << (const quint64)(id);
591 QxtIntrospector::QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal): QObject(parent)
594 QByteArray sig_ba = QMetaObject::normalizedSignature(QByteArray(signal).mid(1));
595 const char * sig=sig_ba.constData();
596 int idx = source->metaObject()->indexOfSignal(sig);
598 qWarning("no such signal: %s",sig_ba.constData());
600 // Our "method" will have the first ID not used by the superclass.
601 QMetaObject::connect(source, idx, this, QObject::staticMetaObject.methodCount());
602 QObject::connect(source, SIGNAL(destroyed()), peer, SLOT(detachSender()));
603 QList<QByteArray> p = source->metaObject()->method(idx).parameterTypes();
605 for (int i=0; i<ct; i++) argTypes.append(QMetaType::type(p.value(i).constData()));
609 int QxtIntrospector::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
611 _id = QObject::qt_metacall(_c, _id, _a);
614 if (_c == QMetaObject::InvokeMetaMethod)
619 int n = argTypes.size();
620 for (int i=0; i<n; i++) v[i] = QVariant(argTypes[i], _a[i+1]);
621 peer->call(rpcFunction.toUtf8().constData(), v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]);
629 QPair<QString, QList<QVariant> > QxtRPCPeer::deserialize(QByteArray& data)
632 int pos = data.indexOf('\n');
633 cmd = data.left(pos-1);
634 data = data.mid(pos+1);
635 if (cmd.length()==0) return qMakePair(QString(), QList<QVariant>());
636 cmd.replace(QByteArray("\\n"), QByteArray("\n"));
637 cmd.replace(QByteArray("\\\\"), QByteArray("\\"));
638 QDataStream str(cmd);
640 unsigned char argCount;
643 str >> signal >> argCount;
645 if (str.status() == QDataStream::ReadCorruptData)
648 return qMakePair(QString(), v);
651 for (int i=0; i<argCount; i++)
656 return qMakePair(signal, v);
660 bool QxtRPCPeer::canDeserialize(const QByteArray& buffer) const
662 if (buffer.indexOf('\n') == -1)
673 QIODevice * QxtRPCPeer::socket()
675 if (qxt_d().m_rpctype == Server)return 0;
676 return qxt_d().m_peer;
682 QTcpSocket * QxtRPCPeer::incomingConnection ( int socketDescriptor )
684 QTcpSocket * t = new QTcpSocket;
685 t->setSocketDescriptor (socketDescriptor);
692 const QTcpSocket * QxtRPCPeer::clientSocket(quint64 id) const
694 if (qxt_d().m_rpctype != Server)
697 return qxt_d().m_clients[(QTcpSocket*)(id)]->socket;
699 QList<quint64> QxtRPCPeer::clients()
702 foreach(QObject * o,qxt_d().m_clients.keys ())
704 list.append((quint64)o);