1 /******************************************************************************
2 
3   This source file is part of the MoleQueue project.
4 
5   Copyright 2012 Kitware, Inc.
6 
7   This source code is released under the New BSD License, (the "License").
8 
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14 
15 ******************************************************************************/
16 
17 #include "message.h"
18 
19 #include "connection.h"
20 #include "messageidmanager_p.h"
21 
22 #include <QtCore/QJsonArray>
23 #include <QtCore/QJsonDocument>
24 #include <QtCore/QStringList>
25 #include <QtCore/QDebug>
26 
27 namespace MoleQueue {
28 
29 // Dummy value returned by *Ref() functions called with invalid types.
30 QJsonValue dummyValue(QJsonValue::Null);
31 
Message(Connection * conn,EndpointIdType endpoint_)32 Message::Message(Connection *conn, EndpointIdType endpoint_)
33   : m_type(Invalid),
34     m_errorCode(0),
35     m_connection(conn),
36     m_endpoint(endpoint_)
37 {
38 }
39 
Message(Message::MessageType type_,Connection * conn,EndpointIdType endpoint_)40 Message::Message(Message::MessageType type_, Connection *conn,
41                  EndpointIdType endpoint_)
42   : m_type(type_),
43     m_errorCode(0),
44     m_connection(conn),
45     m_endpoint(endpoint_)
46 {
47 }
48 
Message(const QJsonObject & rawJson,Connection * conn,EndpointIdType endpoint_)49 Message::Message(const QJsonObject &rawJson, Connection *conn,
50                  EndpointIdType endpoint_)
51   : m_type(Raw),
52     m_errorCode(0),
53     m_rawJson(rawJson),
54     m_connection(conn),
55     m_endpoint(endpoint_)
56 {
57 }
58 
Message(const Message & other)59 Message::Message(const Message &other)
60   : m_type(other.m_type),
61     m_method(other.m_method),
62     m_id(other.m_id),
63     m_params(other.m_params),
64     m_result(other.m_result),
65     m_errorCode(other.m_errorCode),
66     m_errorMessage(other.m_errorMessage),
67     m_errorData(other.m_errorData),
68     m_rawJson(other.m_rawJson),
69     m_connection(other.m_connection),
70     m_endpoint(other.m_endpoint)
71 {
72 }
73 
operator =(const Message & other)74 Message& Message::operator=(const Message &other)
75 {
76   m_type = other.m_type;
77   m_method = other.m_method;
78   m_id = other.m_id;
79   m_params = other.m_params;
80   m_result = other.m_result;
81   m_errorCode = other.m_errorCode;
82   m_errorMessage = other.m_errorMessage;
83   m_errorData = other.m_errorData;
84   m_rawJson = other.m_rawJson;
85   m_connection = other.m_connection;
86   m_endpoint = other.m_endpoint;
87   return *this;
88 }
89 
type() const90 Message::MessageType Message::type() const
91 {
92   return m_type;
93 }
94 
method() const95 QString Message::method() const
96 {
97   if (!checkType(Q_FUNC_INFO, Request | Notification | Response | Error))
98     return QString();
99 
100   return m_method;
101 }
102 
setMethod(const QString & m)103 void Message::setMethod(const QString &m)
104 {
105   if (!checkType(Q_FUNC_INFO, Request | Notification | Response | Error))
106     return;
107 
108   m_method = m;
109 }
110 
params() const111 QJsonValue Message::params() const
112 {
113   if (!checkType(Q_FUNC_INFO, Request | Notification))
114     return QJsonValue();
115 
116   return m_params;
117 }
118 
paramsRef()119 QJsonValue& Message::paramsRef()
120 {
121   if (!checkType(Q_FUNC_INFO, Request | Notification))
122     return dummyValue;
123 
124   return m_params;
125 }
126 
setParams(const QJsonArray & p)127 void Message::setParams(const QJsonArray &p)
128 {
129   if (!checkType(Q_FUNC_INFO, Request | Notification))
130     return;
131 
132   m_params = p;
133 }
134 
setParams(const QJsonObject & p)135 void Message::setParams(const QJsonObject &p)
136 {
137   if (!checkType(Q_FUNC_INFO, Request | Notification))
138     return;
139 
140   m_params = p;
141 }
142 
result() const143 QJsonValue Message::result() const
144 {
145   if (!checkType(Q_FUNC_INFO, Response))
146     return QJsonValue();
147 
148   return m_result;
149 }
150 
resultRef()151 QJsonValue& Message::resultRef()
152 {
153   if (!checkType(Q_FUNC_INFO, Response))
154     return dummyValue;
155 
156   return m_result;
157 }
158 
setResult(const QJsonValue & r)159 void Message::setResult(const QJsonValue &r)
160 {
161   if (!checkType(Q_FUNC_INFO, Response))
162     return;
163 
164   m_result = r;
165 }
166 
errorCode() const167 int Message::errorCode() const
168 {
169   if (!checkType(Q_FUNC_INFO, Error))
170     return 0;
171 
172   return m_errorCode;
173 }
174 
setErrorCode(int e)175 void Message::setErrorCode(int e)
176 {
177   if (!checkType(Q_FUNC_INFO, Error))
178     return;
179 
180   m_errorCode = e;
181 }
182 
errorMessage() const183 QString Message::errorMessage() const
184 {
185   if (!checkType(Q_FUNC_INFO, Error))
186     return QString();
187 
188   return m_errorMessage;
189 }
190 
setErrorMessage(const QString & e)191 void Message::setErrorMessage(const QString &e)
192 {
193   if (!checkType(Q_FUNC_INFO,Error))
194     return;
195 
196   m_errorMessage = e;
197 }
198 
errorData() const199 QJsonValue Message::errorData() const
200 {
201   if (!checkType(Q_FUNC_INFO, Error))
202     return QJsonValue();
203 
204   return m_errorData;
205 }
206 
errorDataRef()207 QJsonValue& Message::errorDataRef()
208 {
209   if (!checkType(Q_FUNC_INFO, Error))
210     return dummyValue;
211 
212   return m_errorData;
213 }
214 
setErrorData(const QJsonValue & e)215 void Message::setErrorData(const QJsonValue &e)
216 {
217   if (!checkType(Q_FUNC_INFO, Error))
218     return;
219 
220   m_errorData = e;
221 }
222 
id() const223 MessageIdType Message::id() const
224 {
225   if (!checkType(Q_FUNC_INFO, Request | Response | Error))
226     return MessageIdType();
227 
228   return m_id;
229 }
230 
setId(const MessageIdType & i)231 void Message::setId(const MessageIdType &i)
232 {
233   if (!checkType(Q_FUNC_INFO, Request | Response | Error))
234     return;
235 
236   m_id = i;
237 }
238 
connection() const239 Connection* Message::connection() const
240 {
241   return m_connection;
242 }
243 
setConnection(Connection * c)244 void Message::setConnection(Connection* c)
245 {
246   m_connection = c;
247 }
248 
endpoint() const249 EndpointIdType Message::endpoint() const
250 {
251   return m_endpoint;
252 }
253 
setEndpoint(const EndpointIdType & e)254 void Message::setEndpoint(const EndpointIdType &e)
255 {
256   m_endpoint = e;
257 }
258 
toJsonObject() const259 QJsonObject Message::toJsonObject() const
260 {
261   QJsonObject obj;
262 
263   switch (m_type) {
264   case MoleQueue::Message::Request:
265     obj.insert("jsonrpc", QLatin1String("2.0"));
266     obj.insert("method", m_method);
267     if ((m_params.isObject() && !m_params.toObject().isEmpty()) ||
268         (m_params.isArray() && !m_params.toArray().isEmpty())) {
269       obj.insert("params", m_params);
270     }
271     obj.insert("id", m_id);
272     break;
273   case MoleQueue::Message::Notification:
274     obj.insert("jsonrpc", QLatin1String("2.0"));
275     obj.insert("method", m_method);
276     if ((m_params.isObject() && !m_params.toObject().isEmpty()) ||
277         (m_params.isArray() && !m_params.toArray().isEmpty())) {
278       obj.insert("params", m_params);
279     }
280     break;
281   case MoleQueue::Message::Response:
282     obj.insert("jsonrpc", QLatin1String("2.0"));
283     obj.insert("result", m_result);
284     obj.insert("id", m_id);
285     break;
286   case MoleQueue::Message::Error: {
287     QJsonObject errorObject;
288     errorObject.insert("code", m_errorCode);
289     errorObject.insert("message", m_errorMessage);
290     if (!m_errorData.isNull())
291       errorObject.insert("data", m_errorData);
292 
293     obj.insert("jsonrpc", QLatin1String("2.0"));
294     obj.insert("error", errorObject);
295     obj.insert("id", m_id);
296     }
297     break;
298   case MoleQueue::Message::Raw:
299     obj = m_rawJson;
300     break;
301   case MoleQueue::Message::Invalid:
302     qWarning() << "Cannot convert invalid message to a JSON object.";
303     break;
304   }
305   return obj;
306 }
307 
toJson() const308 PacketType Message::toJson() const
309 {
310   QJsonDocument doc(toJsonObject());
311   return PacketType(doc.toJson());
312 }
313 
send()314 bool Message::send()
315 {
316   if (m_type == Invalid || !m_connection || !m_connection->isOpen())
317     return false;
318 
319   if (m_type == Request)
320     m_id = MessageIdManager::registerMethod(m_method);
321 
322   return m_connection->send(toJson(), m_endpoint);
323 }
324 
generateResponse() const325 Message Message::generateResponse() const
326 {
327   if (!checkType(Q_FUNC_INFO, Request))
328     return Message();
329 
330   Message resp(Response, m_connection, m_endpoint);
331   resp.m_method     = m_method;
332   resp.m_id         = m_id;
333   return resp;
334 }
335 
generateErrorResponse() const336 Message Message::generateErrorResponse() const
337 {
338   if (!checkType(Q_FUNC_INFO, Request | Raw | Invalid))
339     return Message();
340 
341   Message resp(Error, m_connection, m_endpoint);
342   resp.m_method     = m_method;
343   resp.m_id         = m_id;
344   return resp;
345 }
346 
parse()347 bool Message::parse()
348 {
349   Message message;
350   return parse(message);
351 }
352 
parse(Message & errorMessage_)353 bool Message::parse(Message &errorMessage_)
354 {
355   // Can only parse Raw types -- return true if this message is already parsed
356   // or invalid.
357   if (m_type != Raw)
358     return true;
359 
360   // Validate the message
361   QStringList errors;
362 
363   // jsonrpc must equal "2.0"
364   if (!m_rawJson.contains("jsonrpc"))
365     errors << "jsonrpc key missing.";
366   if (!m_rawJson.value("jsonrpc").isString())
367     errors << "jsonrpc key must be a string.";
368   if (m_rawJson.value("jsonrpc").toString() != "2.0") {
369     errors << QString("Unrecognized jsonrpc string: %1")
370               .arg(m_rawJson.value("jsonrpc").toString());
371   }
372 
373   // Must have either id or method
374   if (!m_rawJson.contains("id") && !m_rawJson.contains("method"))
375     errors << "Missing both id and method.";
376 
377   // If method is present, it must be a string.
378   QString method_;
379   if (m_rawJson.contains("method")) {
380     if (!m_rawJson.value("method").isString())
381       errors << "method must be a string.";
382     else
383       method_ = m_rawJson.value("method").toString();
384   }
385   else {
386     // Lookup method for response/error.
387     method_ = MessageIdManager::lookupMethod(m_rawJson.value("id"));
388   }
389 
390   // If any errors have occurred, prep the response:
391   if (!errors.empty()) {
392     errors.prepend("Invalid request:");
393     QJsonObject errorDataObject;
394     errorDataObject.insert("description", errors.join(" "));
395     errorDataObject.insert("request", m_rawJson);
396     errorMessage_ = generateErrorResponse();
397     errorMessage_.setErrorCode(-32600);
398     errorMessage_.setErrorMessage("Invalid request");
399     errorMessage_.setErrorData(errorDataObject);
400     return false;
401   }
402 
403   // Results, errors, and notifications cannot return errors. Parse them
404   // as best we can and return true.
405   if (m_rawJson.contains("result")) {
406     interpretResponse(m_rawJson, method_);
407     return true;
408   }
409   else if (m_rawJson.contains("error")) {
410     interpretError(m_rawJson, method_);
411     return true;
412   }
413   else if (!m_rawJson.contains("id")) {
414     interpretNotification(m_rawJson);
415     return true;
416   }
417 
418   // Assume anything else is a request.
419   return interpretRequest(m_rawJson, errorMessage_);
420 }
421 
422 inline
checkType(const char * method_,MessageTypes validTypes) const423 bool Message::checkType(const char *method_, MessageTypes validTypes) const
424 {
425   if (m_type & validTypes)
426     return true;
427 
428   qWarning() << "Invalid message type in call.\n"
429              << "  Method:" << method_ << "\n"
430              << "  Valid types:" << validTypes << "\n"
431              << "  Actual type:" << m_type;
432   return false;
433 }
434 
interpretRequest(const QJsonObject & json,Message & errorMessage_)435 bool Message::interpretRequest(const QJsonObject &json, Message &errorMessage_)
436 {
437   QStringList errors;
438 
439   // method must exist and be a string.
440   if (!json.value("method").isString())
441     errors << "method is not a string.";
442 
443   // id must be present.
444   if (!json.contains("id"))
445     errors << "id missing.";
446 
447   // params is optional, but must be structured if present.
448   if (json.contains("params") &&
449       !json.value("params").isArray() &&
450       !json.value("params").isObject()) {
451     errors << "params must be structured if present.";
452   }
453 
454   if (!errors.empty()) {
455     errors.prepend("Invalid request:");
456     QJsonObject errorDataObject;
457     errorDataObject.insert("description", errors.join(" "));
458     errorDataObject.insert("request", json);
459     errorMessage_ = generateErrorResponse();
460     errorMessage_.setErrorCode(-32600);
461     errorMessage_.setErrorMessage("Invalid request");
462     errorMessage_.setErrorData(errorDataObject);
463     return false;
464   }
465 
466   m_type = Request;
467   m_method = json.value("method").toString();
468   if (json.contains("params"))
469     m_params = json.value("params");
470   else
471     m_params = QJsonValue();
472   m_id = MessageIdType(json.value("id"));
473   return true;
474 }
475 
interpretNotification(const QJsonObject & json)476 void Message::interpretNotification(const QJsonObject &json)
477 {
478   m_type = Notification;
479   m_method = json.value("method").toString();
480   if (json.contains("params"))
481     m_params = json.value("params");
482   else
483     m_params = QJsonValue();
484   m_id = MessageIdType();
485   return;
486 }
487 
interpretResponse(const QJsonObject & json,const QString & method_)488 void Message::interpretResponse(const QJsonObject &json, const QString &method_)
489 {
490   m_type = Response;
491   m_method = method_;
492   m_result = json.value("result");
493   m_id = json.value("id");
494   return;
495 }
496 
interpretError(const QJsonObject & json,const QString & method_)497 void Message::interpretError(const QJsonObject &json, const QString &method_)
498 {
499   m_type = Error;
500   m_method = method_;
501   m_id = json.value("id");
502 
503   // We cannot send an error reply if we receive a malformated error message.
504   // If this happens, generate a server error (-32000) with the original error
505   // member as the error data (there must be an error member defined for this
506   // function to be called)
507   QStringList errors;
508   QJsonValue errorValue = json.value("error");
509   if (!errorValue.isObject()) {
510     errors << "error must be an object.";
511   }
512   else {
513     QJsonObject errorObject = errorValue.toObject();
514 
515     // error.code validation
516     if (!errorObject.contains("code")) {
517       errors << "error.code missing.";
518     }
519     else {
520       // Check that error.code is integral. There is no QJsonValue.isInt()
521       // method, only isDouble, so the test is more complicated than it should
522       // be...
523       if (!errorObject.value("code").isDouble()) {
524         errors << "error.code is not numeric.";
525       }
526       else {
527         double code = errorObject.value("code").toDouble();
528         if (qAbs(code - static_cast<double>(static_cast<int>(code))) > 1e-5)
529           errors << "error.code is not integral.";
530         else
531           m_errorCode = static_cast<int>(code);
532       }
533     }
534 
535     // error.message validation
536     if (!errorObject.contains("message")) {
537       errors << "error.message missing.";
538     }
539     else {
540       if (!errorObject.value("message").isString())
541         errors << "error.message is not a string.";
542       else
543         m_errorMessage = errorObject.value("message").toString();
544     }
545 
546     if (errorObject.contains("data"))
547       m_errorData = errorObject.value("data");
548   }
549 
550   // If any errors occured, reset the error members to a server error.
551   if (!errors.empty()) {
552     m_errorCode = -32000;
553     m_errorMessage = "Server error";
554 
555     QJsonObject errorDataObject;
556     errors.prepend("Malformed error response:");
557     errorDataObject.insert("description", errors.join(" "));
558     errorDataObject.insert("origMessage", errorValue);
559     m_errorData = errorDataObject;
560   }
561 }
562 
563 } // namespace MoleQueue
564