1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 #include "JSONRPCUtils.h"
12 #include "XBDateTime.h"
13 #include "playlists/SmartPlayList.h"
14 #include "utils/JSONVariantParser.h"
15 #include "utils/JSONVariantWriter.h"
16 #include "utils/SortUtils.h"
17 #include "utils/StringUtils.h"
18 #include "utils/Variant.h"
19 
20 #include <stdlib.h>
21 #include <string.h>
22 #include <vector>
23 
24 namespace JSONRPC
25 {
26   /*!
27    \brief Possible value types of a parameter or return type
28    */
29   enum JSONSchemaType
30   {
31     NullValue = 0x01,
32     StringValue = 0x02,
33     NumberValue = 0x04,
34     IntegerValue = 0x08,
35     BooleanValue = 0x10,
36     ArrayValue = 0x20,
37     ObjectValue = 0x40,
38     AnyValue = 0x80
39   };
40 
41   /*!
42    \ingroup jsonrpc
43    \brief Helper class containing utility methods to handle
44    json rpc method calls.*/
45   class CJSONUtils
46   {
47   public:
MillisecondsToTimeObject(int time,CVariant & result)48     static void MillisecondsToTimeObject(int time, CVariant &result)
49     {
50       int ms = time % 1000;
51       result["milliseconds"] = ms;
52       time = (time - ms) / 1000;
53 
54       int s = time % 60;
55       result["seconds"] = s;
56       time = (time - s) / 60;
57 
58       int m = time % 60;
59       result["minutes"] = m;
60       time = (time -m) / 60;
61 
62       result["hours"] = time;
63     }
64 
65   protected:
HandleLimits(const CVariant & parameterObject,CVariant & result,int size,int & start,int & end)66     static void HandleLimits(const CVariant &parameterObject, CVariant &result, int size, int &start, int &end)
67     {
68       if (size < 0)
69         size = 0;
70 
71       start = (int)parameterObject["limits"]["start"].asInteger();
72       end   = (int)parameterObject["limits"]["end"].asInteger();
73       end = (end <= 0 || end > size) ? size : end;
74       start = start > end ? end : start;
75 
76       result["limits"]["start"] = start;
77       result["limits"]["end"]   = end;
78       result["limits"]["total"] = size;
79     }
80 
ParseSorting(const CVariant & parameterObject,SortBy & sortBy,SortOrder & sortOrder,SortAttribute & sortAttributes)81     static bool ParseSorting(const CVariant &parameterObject, SortBy &sortBy, SortOrder &sortOrder, SortAttribute &sortAttributes)
82     {
83       std::string method = parameterObject["sort"]["method"].asString();
84       std::string order = parameterObject["sort"]["order"].asString();
85       StringUtils::ToLower(method);
86       StringUtils::ToLower(order);
87 
88       // parse the sort attributes
89       sortAttributes = SortAttributeNone;
90       if (parameterObject["sort"]["ignorearticle"].asBoolean())
91         sortAttributes = static_cast<SortAttribute>(sortAttributes | SortAttributeIgnoreArticle);
92       if (parameterObject["sort"]["useartistsortname"].asBoolean())
93         sortAttributes = static_cast<SortAttribute>(sortAttributes | SortAttributeUseArtistSortName);
94 
95       // parse the sort order
96       sortOrder = SortUtils::SortOrderFromString(order);
97       if (sortOrder == SortOrderNone)
98         return false;
99 
100       // parse the sort method
101       sortBy = SortUtils::SortMethodFromString(method);
102 
103       return true;
104     }
105 
ParseLimits(const CVariant & parameterObject,int & limitStart,int & limitEnd)106     static void ParseLimits(const CVariant &parameterObject, int &limitStart, int &limitEnd)
107     {
108       limitStart = (int)parameterObject["limits"]["start"].asInteger();
109       limitEnd = (int)parameterObject["limits"]["end"].asInteger();
110     }
111 
112     /*!
113      \brief Checks if the given object contains a parameter
114      \param parameterObject Object to check for a parameter
115      \param key Possible name of the parameter
116      \param position Possible position of the parameter
117      \return True if the parameter is available otherwise false
118 
119      Checks the given object for a parameter with the given key (if
120      the given object is not an array) or for a parameter at the
121      given position (if the given object is an array).
122      */
ParameterExists(const CVariant & parameterObject,const std::string & key,unsigned int position)123     static inline bool ParameterExists(const CVariant& parameterObject,
124                                        const std::string& key,
125                                        unsigned int position)
126     {
127       return IsValueMember(parameterObject, key) ||
128              (parameterObject.isArray() && parameterObject.size() > position);
129     }
130 
131     /*!
132      \brief Checks if the given object contains a value
133      with the given key
134      \param value Value to check for the member
135      \param key Key of the member to check for
136      \return True if the given object contains a member with
137      the given key otherwise false
138      */
IsValueMember(const CVariant & value,const std::string & key)139     static inline bool IsValueMember(const CVariant& value, const std::string& key)
140     {
141       return value.isMember(key);
142     }
143 
144     /*!
145      \brief Returns the json value of a parameter
146      \param parameterObject Object containing all provided parameters
147      \param key Possible name of the parameter
148      \param position Possible position of the parameter
149      \return Json value of the parameter with the given name or at the
150      given position
151 
152      Returns the value of the parameter with the given key (if
153      the given object is not an array) or of the parameter at the
154      given position (if the given object is an array).
155      */
GetParameter(const CVariant & parameterObject,const std::string & key,unsigned int position)156     static inline CVariant GetParameter(const CVariant& parameterObject,
157                                         const std::string& key,
158                                         unsigned int position)
159     {
160       return IsValueMember(parameterObject, key) ? parameterObject[key] : parameterObject[position];
161     }
162 
163     /*!
164      \brief Returns the json value of a parameter or the given
165      default value
166      \param parameterObject Object containing all provided parameters
167      \param key Possible name of the parameter
168      \param position Possible position of the parameter
169      \param fallback Default value of the parameter
170      \return Json value of the parameter with the given name or at the
171      given position or the default value if the parameter does not exist
172 
173      Returns the value of the parameter with the given key (if
174      the given object is not an array) or of the parameter at the
175      given position (if the given object is an array). If the
176      parameter does not exist the given default value is returned.
177      */
GetParameter(const CVariant & parameterObject,const std::string & key,unsigned int position,const CVariant & fallback)178     static inline CVariant GetParameter(const CVariant& parameterObject,
179                                         const std::string& key,
180                                         unsigned int position,
181                                         const CVariant& fallback)
182     {
183       return IsValueMember(parameterObject, key)
184                  ? parameterObject[key]
185                  : ((parameterObject.isArray() && parameterObject.size() > position)
186                         ? parameterObject[position]
187                         : fallback);
188     }
189 
190     /*!
191      \brief Returns the given json value as a string
192      \param value Json value to convert to a string
193      \param defaultValue Default string value
194      \return String value of the given json value or the default value
195      if the given json value is no string
196      */
GetString(const CVariant & value,const char * defaultValue)197     static inline std::string GetString(const CVariant &value, const char* defaultValue)
198     {
199       std::string str = defaultValue;
200       if (value.isString())
201       {
202         str = value.asString();
203       }
204 
205       return str;
206     }
207 
208     /*!
209      \brief Returns a TransportLayerCapability value of the
210      given string representation
211      \param transport String representation of the TransportLayerCapability
212      \return TransportLayerCapability value of the given string representation
213      */
StringToTransportLayer(const std::string & transport)214     static inline TransportLayerCapability StringToTransportLayer(const std::string& transport)
215     {
216       if (transport.compare("Announcing") == 0)
217         return Announcing;
218       if (transport.compare("FileDownloadDirect") == 0)
219         return FileDownloadDirect;
220       if (transport.compare("FileDownloadRedirect") == 0)
221         return FileDownloadRedirect;
222 
223       return Response;
224     }
225 
226     /*!
227      \brief Returns a JSONSchemaType value for the given
228      string representation
229      \param valueType String representation of the JSONSchemaType
230      \return JSONSchemaType value of the given string representation
231      */
StringToSchemaValueType(const std::string & valueType)232     static inline JSONSchemaType StringToSchemaValueType(const std::string& valueType)
233     {
234       if (valueType.compare("null") == 0)
235         return NullValue;
236       if (valueType.compare("string") == 0)
237         return StringValue;
238       if (valueType.compare("number") == 0)
239         return NumberValue;
240       if (valueType.compare("integer") == 0)
241         return IntegerValue;
242       if (valueType.compare("boolean") == 0)
243         return BooleanValue;
244       if (valueType.compare("array") == 0)
245         return ArrayValue;
246       if (valueType.compare("object") == 0)
247         return ObjectValue;
248 
249       return AnyValue;
250     }
251 
252     /*!
253      \brief Returns a string representation for the
254      given JSONSchemaType
255      \param valueType Specific JSONSchemaType
256      \return String representation of the given JSONSchemaType
257      */
SchemaValueTypeToString(JSONSchemaType valueType)258     static inline std::string SchemaValueTypeToString(JSONSchemaType valueType)
259     {
260       std::vector<JSONSchemaType> types = std::vector<JSONSchemaType>();
261       for (unsigned int value = 0x01; value <= (unsigned int)AnyValue; value *= 2)
262       {
263         if (HasType(valueType, (JSONSchemaType)value))
264           types.push_back((JSONSchemaType)value);
265       }
266 
267       std::string strType;
268       if (types.size() > 1)
269         strType.append("[");
270 
271       for (unsigned int index = 0; index < types.size(); index++)
272       {
273         if (index > 0)
274           strType.append(", ");
275 
276         switch (types.at(index))
277         {
278         case StringValue:
279           strType.append("string");
280           break;
281         case NumberValue:
282           strType.append("number");
283           break;
284         case IntegerValue:
285           strType.append("integer");
286           break;
287         case BooleanValue:
288           strType.append("boolean");
289           break;
290         case ArrayValue:
291           strType.append("array");
292           break;
293         case ObjectValue:
294           strType.append("object");
295           break;
296         case AnyValue:
297           strType.append("any");
298           break;
299         case NullValue:
300           strType.append("null");
301           break;
302         default:
303           strType.append("unknown");
304         }
305       }
306 
307       if (types.size() > 1)
308         strType.append("]");
309 
310       return strType;
311     }
312 
313     /*!
314      \brief Converts the given json schema type into
315      a json object
316      \param valueTye json schema type(s)
317      \param jsonObject json object into which the json schema type(s) are stored
318      */
SchemaValueTypeToJson(JSONSchemaType valueType,CVariant & jsonObject)319     static inline void SchemaValueTypeToJson(JSONSchemaType valueType, CVariant &jsonObject)
320     {
321       jsonObject = CVariant(CVariant::VariantTypeArray);
322       for (unsigned int value = 0x01; value <= (unsigned int)AnyValue; value *= 2)
323       {
324         if (HasType(valueType, (JSONSchemaType)value))
325           jsonObject.append(SchemaValueTypeToString((JSONSchemaType)value));
326       }
327 
328       if (jsonObject.size() == 1)
329       {
330         CVariant jsonType = jsonObject[0];
331         jsonObject = jsonType;
332       }
333     }
334 
ValueTypeToString(CVariant::VariantType valueType)335     static inline const char *ValueTypeToString(CVariant::VariantType valueType)
336     {
337       switch (valueType)
338       {
339       case CVariant::VariantTypeString:
340         return "string";
341       case CVariant::VariantTypeDouble:
342         return "number";
343       case CVariant::VariantTypeInteger:
344       case CVariant::VariantTypeUnsignedInteger:
345         return "integer";
346       case CVariant::VariantTypeBoolean:
347         return "boolean";
348       case CVariant::VariantTypeArray:
349         return "array";
350       case CVariant::VariantTypeObject:
351         return "object";
352       case CVariant::VariantTypeNull:
353       case CVariant::VariantTypeConstNull:
354         return "null";
355       default:
356         return "unknown";
357       }
358     }
359 
360     /*!
361      \brief Checks if the parameter with the given name or at
362      the given position is of a certain type
363      \param parameterObject Object containing all provided parameters
364      \param key Possible name of the parameter
365      \param position Possible position of the parameter
366      \param valueType Expected type of the parameter
367      \return True if the specific parameter is of the given type otherwise false
368      */
IsParameterType(const CVariant & parameterObject,const char * key,unsigned int position,JSONSchemaType valueType)369     static inline bool IsParameterType(const CVariant &parameterObject, const char *key, unsigned int position, JSONSchemaType valueType)
370     {
371       if ((valueType & AnyValue) == AnyValue)
372         return true;
373 
374       CVariant parameter;
375       if (IsValueMember(parameterObject, key))
376         parameter = parameterObject[key];
377       else if(parameterObject.isArray() && parameterObject.size() > position)
378         parameter = parameterObject[position];
379 
380       return IsType(parameter, valueType);
381     }
382 
383     /*!
384      \brief Checks if the given json value is of the given type
385      \param value Json value to check
386      \param valueType Expected type of the json value
387      \return True if the given json value is of the given type otherwise false
388     */
IsType(const CVariant & value,JSONSchemaType valueType)389     static inline bool IsType(const CVariant &value, JSONSchemaType valueType)
390     {
391       if (HasType(valueType, AnyValue))
392         return true;
393       if (HasType(valueType, StringValue) && value.isString())
394         return true;
395       if (HasType(valueType, NumberValue) && (value.isInteger() || value.isUnsignedInteger() || value.isDouble()))
396         return true;
397       if (HasType(valueType, IntegerValue) && (value.isInteger() || value.isUnsignedInteger()))
398         return true;
399       if (HasType(valueType, BooleanValue) && value.isBoolean())
400         return true;
401       if (HasType(valueType, ArrayValue) && value.isArray())
402         return true;
403       if (HasType(valueType, ObjectValue) && value.isObject())
404         return true;
405 
406       return value.isNull();
407     }
408 
409     /*!
410      \brief Sets the value of the given json value to the
411      default value of the given type
412      \param value Json value to be set
413      \param valueType Type of the default value
414      */
SetDefaultValue(CVariant & value,JSONSchemaType valueType)415     static inline void SetDefaultValue(CVariant &value, JSONSchemaType valueType)
416     {
417       switch (valueType)
418       {
419         case StringValue:
420           value = CVariant("");
421           break;
422         case NumberValue:
423           value = CVariant(CVariant::VariantTypeDouble);
424           break;
425         case IntegerValue:
426           value = CVariant(CVariant::VariantTypeInteger);
427           break;
428         case BooleanValue:
429           value = CVariant(CVariant::VariantTypeBoolean);
430           break;
431         case ArrayValue:
432           value = CVariant(CVariant::VariantTypeArray);
433           break;
434         case ObjectValue:
435           value = CVariant(CVariant::VariantTypeObject);
436           break;
437         default:
438           value = CVariant(CVariant::VariantTypeNull);
439       }
440     }
441 
HasType(JSONSchemaType typeObject,JSONSchemaType type)442     static inline bool HasType(JSONSchemaType typeObject, JSONSchemaType type) { return (typeObject & type) == type; }
443 
ParameterNotNull(const CVariant & parameterObject,const std::string & key)444     static inline bool ParameterNotNull(const CVariant& parameterObject, const std::string& key)
445     {
446       return parameterObject.isMember(key) && !parameterObject[key].isNull();
447     }
448 
449     /*!
450      \brief Copies the values from the jsonStringArray to the stringArray.
451      stringArray is cleared.
452      \param jsonStringArray JSON object representing a string array
453      \param stringArray String array where the values are copied into (cleared)
454      */
CopyStringArray(const CVariant & jsonStringArray,std::vector<std::string> & stringArray)455     static void CopyStringArray(const CVariant &jsonStringArray, std::vector<std::string> &stringArray)
456     {
457       if (!jsonStringArray.isArray())
458         return;
459 
460       stringArray.clear();
461       for (CVariant::const_iterator_array it = jsonStringArray.begin_array(); it != jsonStringArray.end_array(); ++it)
462         stringArray.push_back(it->asString());
463     }
464 
SetFromDBDate(const CVariant & jsonDate,CDateTime & date)465     static void SetFromDBDate(const CVariant &jsonDate, CDateTime &date)
466     {
467       if (!jsonDate.isString())
468         return;
469 
470       if (jsonDate.empty())
471         date.Reset();
472       else
473         date.SetFromDBDate(jsonDate.asString());
474     }
475 
SetFromDBDateTime(const CVariant & jsonDate,CDateTime & date)476     static void SetFromDBDateTime(const CVariant &jsonDate, CDateTime &date)
477     {
478       if (!jsonDate.isString())
479         return;
480 
481       if (jsonDate.empty())
482         date.Reset();
483       else
484         date.SetFromDBDateTime(jsonDate.asString());
485     }
486 
GetXspFiltering(const std::string & type,const CVariant & filter,std::string & xsp)487     static bool GetXspFiltering(const std::string &type, const CVariant &filter, std::string &xsp)
488     {
489       if (type.empty() || !filter.isObject())
490         return false;
491 
492       CVariant xspObj(CVariant::VariantTypeObject);
493       xspObj["type"] = type;
494 
495       if (filter.isMember("field"))
496       {
497         xspObj["rules"]["and"] = CVariant(CVariant::VariantTypeArray);
498         xspObj["rules"]["and"].push_back(filter);
499       }
500       else
501         xspObj["rules"] = filter;
502 
503       CSmartPlaylist playlist;
504       return playlist.Load(xspObj) && playlist.SaveAsJson(xsp, false);
505     }
506   };
507 }
508