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