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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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 ¶meterObject, 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