f976605792e115cee6bf1cbd74b1655b20d1eef4
[quassel.git] / src / contrib / qxt / qxtrpcpeer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtNetwork module of the Qt eXTension library
6 **
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
9 ** IBM.
10 **
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. 
15 **
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.
20 ** 
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24
25 #include "qxtrpcpeer.h"
26 #include <QObject>
27 #include <QTcpSocket>
28 #include <QTcpServer>
29 #include <QMultiHash>
30 #include <QDebug>
31 #include <QMetaMethod>
32 #include <cassert>
33
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.
38 public:
39     QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal);
40
41     int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
42
43     QString rpcFunction;
44
45 private:
46     QxtRPCPeer* peer;
47     QList<int> argTypes;
48 };
49
50 struct QxtRPCConnection {
51     QTcpSocket* socket;
52     QByteArray buffer;
53     QString lastMethod;
54 };
55
56 class QxtRPCPeerPrivate : public QxtPrivate<QxtRPCPeer> {
57 public:
58     QXT_DECLARE_PUBLIC(QxtRPCPeer);
59
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;
64
65     void processInput(QIODevice* socket, QByteArray& buffer);
66
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;
72
73     typedef QHash<QObject*, QxtRPCConnection*> ConnHash;
74     ConnHash m_clients;
75     QTcpServer* m_server;
76     QIODevice* m_peer;  ///aep: doom
77
78     QByteArray m_buffer;
79     int m_rpctype;
80 };
81
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()));
93 }
94
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()));
106 }
107
108 QxtRPCPeer::QxtRPCPeer(QIODevice* device, RPCTypes type, QObject* parent) : QObject(parent) {
109         if (!device->isOpen())
110                 {
111                 qWarning("QxtRPCPeer::the device you passed is not open!");
112                 }
113
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;
118
119     if (qobject_cast<QTcpSocket *>(device)!=0)
120         {
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)));
125         }
126     QObject::connect(qxt_d().m_peer, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
127     QObject::connect(qxt_d().m_server, SIGNAL(newConnection()), this, SLOT(newConnection()));
128 }
129
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";
133         return;
134     }
135     qxt_d().m_rpctype = type;
136 }
137
138 QxtRPCPeer::RPCTypes QxtRPCPeer::rpcType() const {
139     return (RPCTypes)(qxt_d().m_rpctype);
140 }
141
142 void QxtRPCPeer::connect(QHostAddress addr, int port) {
143     if(qxt_d().m_rpctype == Server) {
144         qWarning() << "QxtRPCPeer: Cannot connect outward in Server mode";
145         return;
146     } else if(qxt_d().m_peer->isOpen ()) {
147         qWarning() << "QxtRPCPeer: Already connected";
148         return;
149     }
150     QTcpSocket * sock  = qobject_cast<QTcpSocket*>(qxt_d().m_peer);
151     assert(sock);
152     sock->connectToHost(addr, port);
153 }
154
155 bool QxtRPCPeer::listen(QHostAddress iface, int port) {
156     if(qxt_d().m_rpctype == Client) {
157         qWarning() << "QxtRPCPeer: Cannot listen in Client mode";
158         return false;
159     } else if(qxt_d().m_rpctype == Peer && qxt_d().m_peer->isOpen ()) {
160         qWarning() << "QxtRPCPeer: Cannot listen while connected to a peer";
161         return false;
162     } else if(qxt_d().m_server->isListening()) {
163         qWarning() << "QxtRPCPeer: Already listening";
164         return false;
165     }
166     return qxt_d().m_server->listen(iface, port);
167 }
168
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";
172         return;
173     } else if(qxt_d().m_rpctype!= Server && id!=0) {
174         qWarning() << "QxtRPCPeer: Must specify a client ID to disconnect";
175         return;
176     }
177     QxtRPCConnection* conn;
178     if(id==0) {
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();
183         delete conn;
184     } else {
185         qWarning() << "QxtRPCPeer: no client with id " << id;
186     }
187 }
188
189 void QxtRPCPeer::disconnectAll() {
190     if(qxt_d().m_rpctype!= Server)
191         disconnectPeer();
192     else {
193         for(QxtRPCPeerPrivate::ConnHash::const_iterator i = qxt_d().m_clients.constBegin(); i!= qxt_d().m_clients.constEnd(); i++) {
194             (*i)->socket->deleteLater();
195             delete *i;
196         }
197         qxt_d().m_clients.clear();
198     }
199 }
200
201 void QxtRPCPeer::stopListening() {
202     if(!qxt_d().m_server->isListening()) {
203         qWarning() << "QxtRPCPeer: Not listening";
204         return;
205     }
206     qxt_d().m_server->close();
207 }
208
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;
215         return false;
216     }
217     QxtIntrospector* spec = new QxtIntrospector(this, sender, signal);
218     if(!rpcFunction.isEmpty()) {
219         spec->rpcFunction = rpcFunction.simplified();
220     } else {
221         spec->rpcFunction = sig;
222     }
223     qxt_d().attachedSignals.insertMulti(sender, spec);
224     return true;
225 }
226
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;
232         return false;
233     }
234
235     QString fn;
236     if(rpcFunction[0] == '1' && rpcFunction.contains('(') && rpcFunction.contains(')')) {
237         fn = QMetaObject::normalizedSignature(rpcFunction.mid(1).constData());
238     } else {
239         fn = rpcFunction.simplified();
240     }
241
242     qxt_d().attachedSlots[fn].append(QPair<QObject*, int>(recv, recv->metaObject()->indexOfMethod(recv->metaObject()->normalizedSignature(slot).mid(1))));
243     return true;
244 }
245
246 void QxtRPCPeer::detachSender() {
247     detachObject(sender());
248 }
249
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);
258             else
259                 i++;
260         }
261     }
262 }
263
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 {
265     QByteArray rv;
266     QDataStream str(&rv, QIODevice::WriteOnly);
267     str << fn;
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;
278     str << ct;
279     if(ct--) str << p1;
280     if(ct--) str << p2;
281     if(ct--) str << p3;
282     if(ct--) str << p4;
283     if(ct--) str << p5;
284     if(ct--) str << p6;
285     if(ct--) str << p7;
286     if(ct--) str << p8;
287     if(ct--) str << p9;
288     rv.replace(QByteArray("\\"), QByteArray("\\\\"));
289     rv.replace(QByteArray("\n"), QByteArray("\\n"));
290     rv.append("\n");
291     return rv;
292 }
293
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) {
295
296     QByteArray sig=QMetaObject::normalizedSignature(signal);
297
298     if(!qxt_d().m_peer->isOpen ())
299                 {
300                 qWarning("can't call on a closed device");
301                  return;
302                 }
303     qxt_d().m_peer->write(serialize(sig, p1, p2, p3, p4, p5, p6, p7, p8, p9));
304 }
305
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));
310         if(!conn) {
311             qWarning() << "QxtRPCPeer: no client with id" << id;
312         } else {
313             conn->socket->write(c);
314         }
315     }
316 }
317
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);
320 }
321
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();
324     cs.removeAll(id);
325     callClientList(cs, fn, p1, p2, p3, p4, p5, p6, p7, p8);
326 }
327
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 {
330     QByteArray sig;
331     int numParams;
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));
337     }
338 }
339
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 {
341     QByteArray sig;
342     int numParams;
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));
348     }
349 }
350 #undef QXT_ARG
351
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();
358             next->deleteLater();
359         } else {
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();
368         }
369     } else {
370         QxtRPCConnection* conn = new QxtRPCConnection;
371         conn->socket = next;
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));
377     }
378 }
379
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);
384         return;
385     } else {
386         QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
387         if(!conn) {
388             qWarning() << "QxtRPCPeer: Unrecognized client object connected to dataAvailable";
389             return;
390         }
391         conn->buffer.append(conn->socket->readAll());
392         qxt_d().processInput(conn->socket, (conn->buffer));
393         return;
394     }
395     qWarning() << "QxtRPCPeer: Unrecognized peer object connected to dataAvailable";
396 }
397
398 void QxtRPCPeer::disconnectSender() {
399     QxtRPCConnection* conn = qxt_d().m_clients.value(sender());
400     if(!conn) {
401         if(qxt_d().m_peer!= qobject_cast<QTcpSocket*>(sender())) {
402             qWarning() << "QxtRPCPeer: Unrecognized object connected to disconnectSender";
403             return;
404         }
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()));
410         return;
411     }
412     conn->buffer.append(conn->socket->readAll());
413     conn->buffer.append("\n");
414     qxt_d().processInput(conn->socket, conn->buffer);
415     conn->socket->deleteLater();
416     delete conn;
417     qxt_d().m_clients.remove(sender());
418 }
419
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";
426                 if(socket == m_peer)
427                     qxt_p().disconnectPeer();
428                 else
429                     qxt_p().disconnectPeer((quint64)(socket));
430                 return;
431             }
432             continue;
433         }
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]);
437         } else {
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]);
439         }
440     }
441 }
442
443 QList<quint64> QxtRPCPeer::clients() const {
444     QList<quint64> rv;
445     QList<QObject*> cs = qxt_d().m_clients.keys();
446     foreach(QObject* id, cs) rv << (const quint64)(id);
447     return rv;
448 }
449
450 QxtIntrospector::QxtIntrospector(QxtRPCPeer* parent, QObject* source, const char* signal): QObject(parent) {
451     peer = 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);
456     if(idx<0)
457          qWarning("no such signal: %s",sig_ba.constData());
458
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();
463     int ct = p.count();
464     for(int i=0; i<ct; i++) argTypes.append(QMetaType::type(p.value(i).constData()));
465 }
466
467 int QxtIntrospector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) {
468     _id = QObject::qt_metacall(_c, _id, _a);
469     if (_id < 0)
470         return _id;
471     if (_c == QMetaObject::InvokeMetaMethod) {
472         if(_id==0) {
473             QVariant v[9];
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]);
477         }
478         _id -= 1;
479     }
480     return _id;
481 }
482
483 QPair<QString, QList<QVariant> > QxtRPCPeer::deserialize(QByteArray& data) {
484     QByteArray cmd;
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);
492     QString signal;
493     unsigned char argCount;
494     QList<QVariant> v;
495     QVariant t;
496     str >> signal >> argCount;
497
498     if(str.status() == QDataStream::ReadCorruptData) {
499         v << QVariant();
500         return qMakePair(QString(), v);
501     }
502
503     for(int i=0; i<argCount; i++) {
504         str >> t;
505         v << t;
506     }
507     return qMakePair(signal, v);
508 }
509
510 bool QxtRPCPeer::canDeserialize(const QByteArray& buffer) const {
511         if (buffer.indexOf('\n') == -1)
512                 {
513                 return false;
514                 }
515     return true;
516         
517 }