1 #include <QAbstractSocket>
2 #include <QTextCodec>
3 #include <QSocketNotifier>
4
5 // read/write
6 #ifdef _WIN32
7 # include <io.h>
8 # include "stdinreader.h"
9 #else
10 # include <unistd.h>
11 #endif
12
13 #include "msgpackiodevice.h"
14 #include "util.h"
15 #include "msgpackrequest.h"
16
17 namespace NeovimQt {
18
19 /**
20 * \class NeovimQt::MsgpackIODevice
21 *
22 * \brief A msgpack-rpc channel build on top of QIODevice
23 *
24 */
25
26 /**
27 * Build a MsgpackIODevice that reads from stdin and writes to
28 * stdout
29 */
fromStdinOut(QObject * parent)30 MsgpackIODevice* MsgpackIODevice::fromStdinOut(QObject *parent)
31 {
32 MsgpackIODevice *rpc = new MsgpackIODevice(NULL, parent);
33 msgpack_packer_init(&rpc->m_pk, rpc, (msgpack_packer_write)MsgpackIODevice::msgpack_write_to_stdout);
34 #ifdef _WIN32
35 StdinReader *rsn = new StdinReader(msgpack_unpacker_buffer_capacity(&rpc->m_uk), rpc);
36 connect(rsn, &StdinReader::dataAvailable,
37 rpc, &MsgpackIODevice::dataAvailableStdin);
38 rsn->start();
39 #else
40 QSocketNotifier *rsn = new QSocketNotifier(0, QSocketNotifier::Read, rpc);
41 connect(rsn, &QSocketNotifier::activated,
42 rpc, &MsgpackIODevice::dataAvailableFd);
43 #endif
44 return rpc;
45 }
46
MsgpackIODevice(QIODevice * dev,QObject * parent)47 MsgpackIODevice::MsgpackIODevice(QIODevice *dev, QObject *parent)
48 :QObject(parent), m_reqid(0), m_dev(dev), m_encoding(0), m_reqHandler(0), m_error(NoError)
49 {
50 qRegisterMetaType<MsgpackError>("MsgpackError");
51 msgpack_unpacker_init(&m_uk, MSGPACK_UNPACKER_INIT_BUFFER_SIZE);
52
53 if (m_dev) {
54 // MSVC: wont build without the (mspack_packer_write) cast
55 msgpack_packer_init(&m_pk, this, (msgpack_packer_write)MsgpackIODevice::msgpack_write_to_dev);
56
57 m_dev->setParent(this);
58 connect(m_dev, &QAbstractSocket::readyRead,
59 this, &MsgpackIODevice::dataAvailable);
60
61 if ( !m_dev->isSequential() ) {
62 setError(InvalidDevice, tr("IO device needs to be sequential"));
63 return;
64 }
65 }
66 }
67
~MsgpackIODevice()68 MsgpackIODevice::~MsgpackIODevice()
69 {
70 //msgpack_packer_destroy(&m_pk);
71 msgpack_unpacker_destroy(&m_uk);
72 }
73
isOpen()74 bool MsgpackIODevice::isOpen()
75 {
76 if (m_error == InvalidDevice) {
77 return false;
78 } else if (m_dev) {
79 return m_dev->isOpen();
80 } else {
81 return true;
82 }
83 }
84
85 /** The encoding used by the MsgpackIODevice::encode and MsgpackIODevice::decode methods @see setEncoding */
encoding() const86 QByteArray MsgpackIODevice::encoding() const
87 {
88 if (m_encoding) {
89 return m_encoding->name();
90 }
91 return QByteArray();
92 }
93
94 /**
95 * Set encoding for strings in this RPC, if it not set we
96 * fall back to utf8
97 *
98 * \see QTextCode
99 */
setEncoding(const QByteArray & name)100 bool MsgpackIODevice::setEncoding(const QByteArray& name)
101 {
102 m_encoding = QTextCodec::codecForName(name);
103 if (!m_encoding) {
104 setError(UnsupportedEncoding, QString("Unsupported encoding (%1)").arg(QString::fromLatin1(name)));
105 return false;
106 }
107 return true;
108 }
109
msgpack_write_to_stdout(void * data,const char * buf,unsigned long int len)110 int MsgpackIODevice::msgpack_write_to_stdout(void* data, const char* buf, unsigned long int len)
111 {
112 MsgpackIODevice *c = static_cast<MsgpackIODevice*>(data);
113 qint64 bytes = write(1, buf, len);
114 if (bytes == -1) {
115 c->setError(InvalidDevice, tr("Error writing to device"));
116 }
117 return bytes;
118 }
119
msgpack_write_to_dev(void * data,const char * buf,unsigned long int len)120 int MsgpackIODevice::msgpack_write_to_dev(void* data, const char* buf, unsigned long int len)
121 {
122 MsgpackIODevice *c = static_cast<MsgpackIODevice*>(data);
123 qint64 bytes = c->m_dev->write(buf, len);
124 if (bytes == -1) {
125 c->setError(InvalidDevice, tr("Error writing to device"));
126 }
127 return bytes;
128 }
129
130 /**
131 * Process incoming data.
132 *
133 * \see fromStdinOut() and StdinReader()
134 */
dataAvailableStdin(const QByteArray & data)135 void MsgpackIODevice::dataAvailableStdin(const QByteArray& data)
136 {
137 if ( (quint64)data.length() > msgpack_unpacker_buffer_capacity(&m_uk)) {
138 setError(InvalidDevice, tr("Error when reading from stdin, BUG(buffered data exceeds capaciy)"));
139 return;
140 } else if ( data.length() > 0 ) {
141 memcpy(msgpack_unpacker_buffer(&m_uk), data.constData(), data.length());
142 msgpack_unpacker_buffer_consumed(&m_uk, data.length());
143 msgpack_unpacked result;
144 msgpack_unpacked_init(&result);
145 while(msgpack_unpacker_next(&m_uk, &result)) {
146 dispatch(result.data);
147 }
148 }
149 }
150
151 /**
152 * Process incoming data from the given fd.
153 *
154 * \see fromStdinOut() and QSocketNotifier()
155 */
dataAvailableFd(int fd)156 void MsgpackIODevice::dataAvailableFd(int fd)
157 {
158 if ( msgpack_unpacker_buffer_capacity(&m_uk) == 0 ) {
159 if ( !msgpack_unpacker_reserve_buffer(&m_uk, 8192 ) ) {
160 qFatal("Could not allocate memory in unpack buffer");
161 return;
162 }
163 }
164
165 qint64 bytes = read(fd, msgpack_unpacker_buffer(&m_uk),
166 msgpack_unpacker_buffer_capacity(&m_uk));
167 if (bytes > 0) {
168 msgpack_unpacker_buffer_consumed(&m_uk, bytes);
169 msgpack_unpacked result;
170 msgpack_unpacked_init(&result);
171 while(msgpack_unpacker_next(&m_uk, &result)) {
172 dispatch(result.data);
173 }
174 } else if (bytes == -1) {
175 setError(InvalidDevice, tr("Error when reading from device"));
176 }
177 }
178
179 /**
180 * Process incoming data from the underlying device
181 *
182 * \see QIODevice()
183 */
dataAvailable()184 void MsgpackIODevice::dataAvailable()
185 {
186 qint64 read = 1;
187 while (read > 0) {
188 if ( msgpack_unpacker_buffer_capacity(&m_uk) == 0 ) {
189 if ( !msgpack_unpacker_reserve_buffer(&m_uk, 8192 ) ) {
190 qFatal("Could not allocate memory in unpack buffer");
191 return;
192 }
193 }
194
195 read = m_dev->read(msgpack_unpacker_buffer(&m_uk), msgpack_unpacker_buffer_capacity(&m_uk));
196 if ( read > 0 ) {
197 msgpack_unpacker_buffer_consumed(&m_uk, read);
198 msgpack_unpacked result;
199 msgpack_unpacked_init(&result);
200 while(msgpack_unpacker_next(&m_uk, &result)) {
201 dispatch(result.data);
202 }
203 }
204 }
205 }
206
207 /**
208 * Send error response for the given request message
209 */
sendError(const msgpack_object & req,const QString & msg)210 void MsgpackIODevice::sendError(const msgpack_object& req, const QString& msg)
211 {
212 if ( req.via.array.ptr[0].via.u64 != 0 ) {
213 qFatal("Errors can only be send as replies to Requests(type=0)");
214 }
215 sendError(req.via.array.ptr[1].via.u64, msg);
216 }
217
sendError(uint64_t msgid,const QString & msg)218 void MsgpackIODevice::sendError(uint64_t msgid, const QString& msg)
219 {
220 // [type(1), msgid, error, result(nil)]
221 msgpack_pack_array(&m_pk, 4);
222 msgpack_pack_int(&m_pk, 1); // 1 = Response
223 msgpack_pack_int(&m_pk, msgid);
224 QByteArray utf8 = msg.toUtf8();
225 msgpack_pack_bin(&m_pk, utf8.size());
226 msgpack_pack_bin_body(&m_pk, utf8.constData(), utf8.size());
227 msgpack_pack_nil(&m_pk);
228 }
229
230 /**
231 * Do some sanity checks and forward message to the proper handler
232 */
dispatch(msgpack_object & req)233 void MsgpackIODevice::dispatch(msgpack_object& req)
234 {
235 //
236 // neovim msgpack rpc calls are
237 // [type(int), msgid(int), method(int), args(array)]
238 //
239
240 if (req.type != MSGPACK_OBJECT_ARRAY) {
241 qDebug() << "Received Invalid msgpack: not an array";
242 return;
243 }
244
245 if (req.via.array.size < 3 || req.via.array.size > 4) {
246 qDebug() << "Received Invalid msgpack: message len MUST be 3 or 4";
247 return;
248 }
249
250 if (req.via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
251 qDebug() << "Received Invalid msgpack: msg type MUST be an integer";
252 return;
253 }
254 uint64_t type = req.via.array.ptr[0].via.u64;
255
256 switch(type) {
257 case 0:
258 if (req.via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
259 qDebug() << "Received Invalid request: msg id MUST be a positive integer";
260 sendError(req, tr("Msg Id must be a positive integer"));
261 return;
262 }
263 if (req.via.array.ptr[2].type != MSGPACK_OBJECT_BIN &&
264 req.via.array.ptr[2].type != MSGPACK_OBJECT_STR) {
265 qDebug() << "Received Invalid request: method MUST be a String" << req.via.array.ptr[2];
266 sendError(req, tr("Method id must be a positive integer"));
267 return;
268 }
269 if (req.via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) {
270 qDebug() << "Invalid request: arguments MUST be an array";
271 sendError(req, tr("Parameters must be an array"));
272 return;
273 }
274 dispatchRequest(req);
275 break;
276 case 1:
277 if (req.via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
278 qDebug() << "Received Invalid response: msg id MUST be a positive integer";
279 return;
280 }
281 dispatchResponse(req);
282 break;
283 case 2:
284 dispatchNotification(req);
285 break;
286 default:
287 qDebug() << "Unsupported msg type" << type;
288 }
289 }
290
291 /**
292 * Handle request message
293 *
294 * [type(0), msgid(uint), method(bin), args([...])]
295 */
dispatchRequest(msgpack_object & req)296 void MsgpackIODevice::dispatchRequest(msgpack_object& req)
297 {
298 uint64_t msgid = req.via.array.ptr[1].via.u64;
299 QByteArray errmsg("Unknown method");
300 QVariant params;
301 QByteArray method;
302
303 if (!m_reqHandler) {
304 goto err;
305 }
306
307 if (decodeMsgpack(req.via.array.ptr[2], method)) {
308 qDebug() << "Found unexpected method in request" << req;
309 goto err;
310 }
311 if (decodeMsgpack(req.via.array.ptr[3], params)) {
312 qDebug() << "Found unexpected parameters in request" << req;
313 goto err;
314 }
315 m_reqHandler->handleRequest(this, msgid, method, params.toList());
316 return;
317
318 err:
319 // Send error reply [type(1), msgid, error, NIL]
320 msgpack_pack_array(&m_pk, 4);
321 msgpack_pack_int(&m_pk, 1);
322 msgpack_pack_int(&m_pk, msgid);
323 msgpack_pack_bin(&m_pk, errmsg.size());
324 msgpack_pack_bin_body(&m_pk, errmsg.constData(), errmsg.size());
325 msgpack_pack_nil(&m_pk);
326 }
327
328 /** Assign a handler for Msgpack-RPC requests */
setRequestHandler(MsgpackRequestHandler * h)329 void MsgpackIODevice::setRequestHandler(MsgpackRequestHandler *h)
330 {
331 m_reqHandler = h;
332 }
333
334 /**
335 * Send back a response [type(1), msgid(uint), error(...), result(...)]
336 */
sendResponse(uint64_t msgid,const QVariant & err,const QVariant & res)337 bool MsgpackIODevice::sendResponse(uint64_t msgid, const QVariant& err, const QVariant& res)
338 {
339 if (!checkVariant(err) || !checkVariant(res)) {
340 qDebug() << "Unable to serialize response" << res;
341 sendError(msgid, tr("Internal server error, could not serialize response"));
342 return false;
343 }
344
345 msgpack_pack_array(&m_pk, 4);
346 msgpack_pack_int(&m_pk, 1);
347 msgpack_pack_int(&m_pk, msgid);
348 send(err);
349 send(res);
350 return true;
351 }
352
353 /**
354 * Send [type(2), method, params]
355 * Returns false if the params could not be serialized
356 */
sendNotification(const QByteArray & method,const QVariantList & params)357 bool MsgpackIODevice::sendNotification(const QByteArray& method, const QVariantList& params)
358 {
359 if (!checkVariant(params)) {
360 return false;
361 }
362
363 msgpack_pack_array(&m_pk, 3);
364 msgpack_pack_int(&m_pk, 2);
365 send(method);
366 send(params);
367 return true;
368 }
369
370 /**
371 * Handle response messages
372 *
373 * [type(1), msgid(uint), error(str?), result(...)]
374 */
dispatchResponse(msgpack_object & resp)375 void MsgpackIODevice::dispatchResponse(msgpack_object& resp)
376 {
377 // [type(1), msgid, error, result]
378 uint64_t msgid = resp.via.array.ptr[1].via.u64;
379
380 if ( !m_requests.contains(msgid) ) {
381 qWarning() << "Received response for unknown message" << msgid;
382 return;
383 }
384
385 MsgpackRequest *req = m_requests.take(msgid);
386 if ( resp.via.array.ptr[2].type != MSGPACK_OBJECT_NIL ) {
387 // Error response
388 QVariant val;
389 if (decodeMsgpack(resp.via.array.ptr[2], val)) {
390 qWarning() << "Error decoding response error object";
391 goto err;
392 }
393 emit req->error(req->id, req->function(), val);
394 } else {
395 QVariant val;
396 if (decodeMsgpack(resp.via.array.ptr[3], val)) {
397 qWarning() << "Error decoding response object";
398 goto err;
399 }
400 emit req->finished(req->id, req->function(), val);
401 }
402 err:
403 req->deleteLater();
404 }
405
406 /** Return list of pending request ids */
pendingRequests() const407 QList<quint32> MsgpackIODevice::pendingRequests() const
408 {
409 return m_requests.keys();
410 }
411
412 /**
413 * [type(2), method, params]
414 *
415 */
dispatchNotification(msgpack_object & nt)416 void MsgpackIODevice::dispatchNotification(msgpack_object& nt)
417 {
418 QByteArray methodName;
419 if (decodeMsgpack(nt.via.array.ptr[1], methodName)) {
420 qDebug() << "Received Invalid notification: event MUST be a String";
421 return;
422 }
423
424 QVariant val;
425 if (decodeMsgpack(nt.via.array.ptr[2], val) ||
426 (QMetaType::Type)val.type() != QMetaType::QVariantList ) {
427 qDebug() << "Unable to unpack notification parameters" << nt;
428 return;
429 }
430 emit notification(methodName, val.toList());
431 }
432
433 /**
434 * Sets latest error code and message for this connector
435 */
setError(MsgpackError err,const QString & msg)436 void MsgpackIODevice::setError(MsgpackError err, const QString& msg)
437 {
438 m_error = err;
439 m_errorString = msg;
440 qWarning() << "MsgpackIO fatal error" << m_errorString;
441 emit error(m_error);
442 }
443
444 /** Return error string for the current error */
errorString() const445 QString MsgpackIODevice::errorString() const
446 {
447 if (m_error) {
448 return m_errorString;
449 } else if (m_dev) {
450 return m_dev->errorString();
451 } else {
452 return QString();
453 }
454 }
455
456 /**
457 * Start an RPC request
458 *
459 * Use send() to pass on each of the call parameters
460 *
461 * Returns a MsgpackRequest object. You can connect to
462 * its finished() SIGNAL to handle the response
463 */
startRequestUnchecked(const QString & method,quint32 argcount)464 MsgpackRequest* MsgpackIODevice::startRequestUnchecked(const QString& method, quint32 argcount)
465 {
466 quint32 msgid = msgId();
467 // [type(0), msgid, method, args]
468 msgpack_pack_array(&m_pk, 4);
469 msgpack_pack_int(&m_pk, 0);
470 msgpack_pack_int(&m_pk, msgid);
471 const QByteArray& utf8 = method.toUtf8();
472 msgpack_pack_bin(&m_pk, utf8.size());
473 msgpack_pack_bin_body(&m_pk, utf8.constData(), utf8.size());
474 msgpack_pack_array(&m_pk, argcount);
475
476 MsgpackRequest *r = new MsgpackRequest( msgid, this);
477 connect(r, &MsgpackRequest::timeout,
478 this, &MsgpackIODevice::requestTimeout);
479 m_requests.insert(msgid, r);
480 return r;
481 }
482
483 /**
484 * Request timed out, discard it
485 */
requestTimeout(quint32 id)486 void MsgpackIODevice::requestTimeout(quint32 id)
487 {
488 if (m_requests.contains(id)) {
489 MsgpackRequest *r = m_requests.take(id);
490 r->deleteLater();
491 qWarning() << "Request" << id << "timed out:" << r->function();
492 }
493 }
494
495 /**
496 * Returns a new msgid that can be used for a msg
497 */
msgId()498 quint32 MsgpackIODevice::msgId()
499 {
500 return this->m_reqid++;
501 }
502
send(int64_t i)503 void MsgpackIODevice::send(int64_t i)
504 {
505 msgpack_pack_int64(&m_pk, i);
506 }
decodeMsgpack(const msgpack_object & in,int64_t & out)507 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, int64_t& out)
508 {
509 if ( in.type != MSGPACK_OBJECT_POSITIVE_INTEGER) {
510 qWarning() << "Attempting to decode as int64_t when type is" << in.type << in;
511 out = -1;
512 return true;
513 }
514 out = in.via.i64;
515 return false;
516 }
517
send(double d)518 void MsgpackIODevice::send(double d)
519 {
520 msgpack_pack_float(&m_pk, d);
521 }
decodeMsgpack(const msgpack_object & in,double & out)522 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, double& out)
523 {
524 if ( in.type != MSGPACK_OBJECT_FLOAT) {
525 qWarning() << "Attempting to decode as double when type is" << in.type << in;
526 out = -1;
527 return true;
528 }
529 out = in.via.f64;
530 return false;
531 }
532
533 /**
534 * Serialise a value into the msgpack stream
535 */
send(const QByteArray & bin)536 void MsgpackIODevice::send(const QByteArray& bin)
537 {
538 msgpack_pack_bin(&m_pk, bin.size());
539 msgpack_pack_bin_body(&m_pk, bin.constData(), bin.size());
540 }
decodeMsgpack(const msgpack_object & in,QByteArray & out)541 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QByteArray& out)
542 {
543 if ( in.type == MSGPACK_OBJECT_BIN) {
544 out = QByteArray(in.via.bin.ptr, in.via.bin.size);
545 } else if ( in.type == MSGPACK_OBJECT_STR) {
546 out = QByteArray(in.via.str.ptr, in.via.str.size);
547 } else {
548 qWarning() << "Attempting to decode as QByteArray when type is" << in.type << in;
549 out = QByteArray();
550 return true;
551 }
552 return false;
553 }
554
555
556 /**
557 * Serialise a valud into the msgpack stream
558 */
send(bool b)559 void MsgpackIODevice::send(bool b)
560 {
561 if ( b ) {
562 msgpack_pack_true(&m_pk);
563 } else {
564 msgpack_pack_false(&m_pk);
565 }
566 }
decodeMsgpack(const msgpack_object & in,bool & out)567 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, bool& out)
568 {
569 if ( in.type != MSGPACK_OBJECT_BOOLEAN) {
570 qWarning() << "Attempting to decode as bool when type is" << in.type << in;
571 out = false;
572 return true;
573 }
574 out = in.via.boolean;
575 return false;
576 }
577
578
send(const QList<QByteArray> & list)579 void MsgpackIODevice::send(const QList<QByteArray>& list)
580 {
581 msgpack_pack_array(&m_pk, list.size());
582 foreach(const QByteArray& elem, list) {
583 send(elem);
584 }
585 }
decodeMsgpack(const msgpack_object & in,QList<QByteArray> & out)586 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QList<QByteArray>& out)
587 {
588 out.clear();
589 if ( in.type != MSGPACK_OBJECT_ARRAY) {
590 qWarning() << "Attempting to decode as QList<QByteArray> when type is" << in.type << in;
591 return true;
592 }
593
594 for (uint64_t i=0; i<in.via.array.size; i++) {
595 QByteArray val;
596 if (decodeMsgpack(in.via.array.ptr[i], val)) {
597 out.clear();
598 return true;
599 }
600 out.append(val);
601 }
602 return false;
603 }
604
decodeMsgpack(const msgpack_object & in,QList<int64_t> & out)605 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QList<int64_t>& out)
606 {
607 out.clear();
608 if ( in.type != MSGPACK_OBJECT_ARRAY) {
609 qWarning() << "Attempting to decode as QList<int64_t> when type is" << in.type << in;
610 return true;
611 }
612
613 for (uint64_t i=0; i<in.via.array.size; i++) {
614 int64_t val;
615 if (decodeMsgpack(in.via.array.ptr[i], val)) {
616 out.clear();
617 return true;
618 }
619 out.append(val);
620 }
621 return false;
622 }
623
decodeMsgpack(const msgpack_object & in,QList<double> & out)624 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QList<double>& out)
625 {
626 out.clear();
627 if ( in.type != MSGPACK_OBJECT_ARRAY) {
628 qWarning() << "Attempting to decode as QList<double> when type is" << in.type << in;
629 return true;
630 }
631
632 for (uint64_t i=0; i<in.via.array.size; i++) {
633 double val;
634 if (decodeMsgpack(in.via.array.ptr[i], val)) {
635 out.clear();
636 return true;
637 }
638 out.append(val);
639 }
640 return false;
641 }
642
643 /**
644 * We use QVariants for RPC functions that use the *Object* type do not use
645 * this in any other conditions
646 */
decodeMsgpack(const msgpack_object & in,QVariant & out)647 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QVariant& out)
648 {
649 switch (in.type) {
650 case MSGPACK_OBJECT_NIL:
651 out = QVariant();
652 break;
653 case MSGPACK_OBJECT_BOOLEAN:
654 out = in.via.boolean;
655 break;
656 case MSGPACK_OBJECT_NEGATIVE_INTEGER:
657 out = QVariant((qint64)in.via.i64);
658 break;
659 case MSGPACK_OBJECT_POSITIVE_INTEGER:
660 out = QVariant((quint64)in.via.u64);
661 break;
662 case MSGPACK_OBJECT_FLOAT:
663 out = in.via.f64;
664 break;
665 case MSGPACK_OBJECT_STR:
666 case MSGPACK_OBJECT_BIN:
667 {
668 QByteArray val;
669 if (decodeMsgpack(in, val)) {
670 qWarning() << "Error unpacking ByteArray as QVariant";
671 out = QVariant();
672 return true;
673 }
674 out = val;
675 }
676 break;
677 case MSGPACK_OBJECT_ARRAY:
678 // Either a QVariantList or a QStringList
679 {
680 QVariantList ls;
681 for (uint64_t i=0; i<in.via.array.size; i++) {
682 QVariant v;
683 bool failed = decodeMsgpack(in.via.array.ptr[i], v);
684 if (failed) {
685 qWarning() << "Error unpacking Map as QVariantList";
686 out = QVariant();
687 return true;
688 }
689 ls.append(v);
690 }
691 out = ls;
692 }
693 break;
694 case MSGPACK_OBJECT_MAP:
695 {
696 QVariantMap m;
697 for (uint64_t i=0; i<in.via.map.size; i++) {
698 QByteArray key;
699 if (decodeMsgpack(in.via.map.ptr[i].key, key)) {
700 qWarning() << "Error decoding Object(Map) key";
701 out = QVariant();
702 return true;
703 }
704 QVariant val;
705 if (decodeMsgpack(in.via.map.ptr[i].val, val)) {
706 qWarning() << "Error decoding Object(Map) value";
707 out = QVariant();
708 return true;
709 }
710 m.insert(key,val);
711 }
712 out = m;
713 }
714 break;
715 case MSGPACK_OBJECT_EXT:
716 if (m_extTypes.contains(in.via.ext.type)) {
717 out = m_extTypes.value(in.via.ext.type)(this, in.via.ext.ptr, in.via.ext.size);
718 if (!out.isValid()) {
719 qWarning() << "EXT unpacking failed" << in.via.ext.type;
720 return true;
721 }
722 } else {
723 out = QVariant();
724 qWarning() << "Unsupported EXT type found in Object" << in.via.ext.type;
725 }
726 break;
727 default:
728 out = QVariant();
729 qWarning() << "Unsupported type found in Object" << in.type << in;
730 return true;
731 }
732 return false;
733 }
734
735 /**
736 * Register a function to decode Msgpack EXT types
737 *
738 * @see msgpackExtDecoder
739 */
registerExtType(int8_t type,msgpackExtDecoder fun)740 void MsgpackIODevice::registerExtType(int8_t type, msgpackExtDecoder fun)
741 {
742 m_extTypes.insert(type, fun);
743 }
744
745 /**
746 * Serialise a value into the msgpack stream
747 *
748 * We use QVariants for RPC functions that use the *Object* type. Do not use
749 * this in any other conditions
750 */
send(const QVariant & var)751 void MsgpackIODevice::send(const QVariant& var)
752 {
753 if (!checkVariant(var)) {
754 msgpack_pack_nil(&m_pk);
755 qWarning() << "Trying to pack unsupported variant type" << var.type() << "packing Nil instead";
756 return;
757 }
758
759 switch((QMetaType::Type)var.type()) {
760 case QMetaType::Void:
761 case QMetaType::UnknownType:
762 msgpack_pack_nil(&m_pk);
763 break;
764 case QMetaType::Bool:
765 send(var.toBool());
766 break;
767 case QMetaType::Int:
768 msgpack_pack_int(&m_pk, var.toInt());
769 break;
770 case QMetaType::UInt:
771 msgpack_pack_unsigned_int(&m_pk, var.toUInt());
772 break;
773 case QMetaType::Long:
774 msgpack_pack_long_long(&m_pk, var.toLongLong());
775 break;
776 case QMetaType::LongLong:
777 msgpack_pack_long_long(&m_pk, var.toLongLong());
778 break;
779 case QMetaType::ULong:
780 msgpack_pack_unsigned_long_long(&m_pk, var.toULongLong());
781 break;
782 case QMetaType::ULongLong:
783 msgpack_pack_unsigned_long_long(&m_pk, var.toULongLong());
784 break;
785 case QMetaType::Float:
786 msgpack_pack_float(&m_pk, var.toFloat());
787 break;
788 case QMetaType::Double:
789 msgpack_pack_double(&m_pk, var.toDouble());
790 break;
791 case QMetaType::QByteArray:
792 send(var.toByteArray());
793 break;
794 case QMetaType::QStringList:
795 msgpack_pack_array(&m_pk, var.toList().size());
796 foreach(const QVariant& elem, var.toList()) {
797 send(elem);
798 }
799 break;
800 case QMetaType::QVariantList:
801 msgpack_pack_array(&m_pk, var.toList().size());
802 foreach(const QVariant& elem, var.toList()) {
803 send(elem);
804 }
805 break;
806 case QMetaType::QVariantMap:
807 {
808 const QVariantMap& m = var.toMap();
809 msgpack_pack_map(&m_pk, m.size());
810 QMapIterator<QString,QVariant> it(m);
811 while(it.hasNext()) {
812 it.next();
813 send(it.key());
814 send(it.value());
815 }
816 }
817 break;
818 case QMetaType::QPoint:
819 // As an array [row, col]
820 msgpack_pack_array(&m_pk, 2);
821 msgpack_pack_int64(&m_pk, var.toPoint().y());
822 msgpack_pack_int64(&m_pk, var.toPoint().x());
823 break;
824 case QMetaType::QString:
825 send(encode(var.toString()));
826 break;
827 default:
828 msgpack_pack_nil(&m_pk);
829 qWarning() << "There is a BUG in the QVariant serializer" << var.type();
830 }
831 }
832
decodeMsgpack(const msgpack_object & in,QPoint & out)833 bool MsgpackIODevice::decodeMsgpack(const msgpack_object& in, QPoint& out)
834 {
835 if ( in.type != MSGPACK_OBJECT_ARRAY || in.via.array.size != 2 ) {
836 goto fail;
837 }
838 int64_t col, row;
839 if (decodeMsgpack(in.via.array.ptr[0], row)) {
840 goto fail;
841 }
842 if (decodeMsgpack(in.via.array.ptr[1], col)) {
843 goto fail;
844 }
845
846 // QPoint is (x,y) neovim Position is (row, col)
847 out = QPoint(col, row);
848 return false;
849
850 fail:
851 qWarning() << "Attempting to decode as QPoint failed" << in.type << in;
852 out = QPoint();
853 return true;
854 }
855
856 /**
857 * Convert string to the proper encoding
858 * \see MsgpackIODevice::setEncoding
859 */
encode(const QString & str)860 QByteArray MsgpackIODevice::encode(const QString& str)
861 {
862 if (m_encoding) {
863 return m_encoding->fromUnicode(str);
864 } else {
865 qWarning() << "Encoding String into MsgpackIODevice without an encoding (defaulting to utf8)";
866 return str.toUtf8();
867 }
868 }
869
870 /**
871 * Decode byte array as string, from Neovim's encoding
872 */
decode(const QByteArray & data)873 QString MsgpackIODevice::decode(const QByteArray& data)
874 {
875 if (m_encoding) {
876 return m_encoding->toUnicode(data);
877 } else {
878 qWarning() << "Decoding String from MsgpackIODevice without an encoding (defaulting to utf8)";
879 return QString::fromUtf8(data);
880 }
881 }
882
883 /**
884 * QVariant can encapsulate some data types that cannot be serialised at as
885 * msgpack data..
886 *
887 * Ideally we would detect such errors early on and avoid sending those objects
888 * entirely - this function checks if QVariant objects are admissible
889 *
890 * Returns true if the QVariant can be serialized
891 */
checkVariant(const QVariant & var)892 bool MsgpackIODevice::checkVariant(const QVariant& var)
893 {
894 switch((QMetaType::Type)var.type()) {
895 case QMetaType::UnknownType:
896 break;
897 case QMetaType::Bool:
898 break;
899 case QMetaType::Int:
900 break;
901 case QMetaType::UInt:
902 break;
903 case QMetaType::Long:
904 break;
905 case QMetaType::LongLong:
906 break;
907 case QMetaType::ULong:
908 break;
909 case QMetaType::ULongLong:
910 break;
911 case QMetaType::Float:
912 break;
913 case QMetaType::QString:
914 break;
915 case QMetaType::Double:
916 break;
917 case QMetaType::QByteArray:
918 break;
919 case QMetaType::QStringList:
920 break;
921 case QMetaType::QVariantList:
922 foreach(const QVariant& elem, var.toList()) {
923 if (!checkVariant(elem)) {
924 return false;
925 }
926 }
927 break;
928 case QMetaType::QVariantMap:
929 {
930 const QVariantMap& m = var.toMap();
931 QMapIterator<QString,QVariant> it(m);
932 while(it.hasNext()) {
933 it.next();
934 if (!checkVariant(it.key())) {
935 return false;
936 }
937 if (!checkVariant(it.value())) {
938 return false;
939 }
940 }
941 }
942 break;
943 case QMetaType::QPoint:
944 break;
945 default:
946 return false;
947 }
948 return true;
949 }
950
951 } // Namespace NeovimQt
952