2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "ServiceDescription.h"
22 #include "JSONServiceDescription.h"
23 #include "utils/log.h"
24 #include "utils/StdString.h"
25 #include "utils/JSONVariantParser.h"
26 #include "utils/StringUtils.h"
28 #include "PlayerOperations.h"
29 #include "PlaylistOperations.h"
30 #include "FileOperations.h"
31 #include "AudioLibrary.h"
32 #include "VideoLibrary.h"
33 #include "GUIOperations.h"
34 #include "AddonsOperations.h"
35 #include "SystemOperations.h"
36 #include "InputOperations.h"
37 #include "XBMCOperations.h"
38 #include "ApplicationOperations.h"
39 #include "PVROperations.h"
40 #include "ProfilesOperations.h"
41 #include "FavouritesOperations.h"
42 #include "TextureOperations.h"
43 #include "SettingsOperations.h"
46 using namespace JSONRPC;
48 map<string, CVariant> CJSONServiceDescription::m_notifications = map<string, CVariant>();
49 CJSONServiceDescription::CJsonRpcMethodMap CJSONServiceDescription::m_actionMap;
50 map<string, JSONSchemaTypeDefinitionPtr> CJSONServiceDescription::m_types = map<string, JSONSchemaTypeDefinitionPtr>();
51 CJSONServiceDescription::IncompleteSchemaDefinitionMap CJSONServiceDescription::m_incompleteDefinitions = CJSONServiceDescription::IncompleteSchemaDefinitionMap();
53 JsonRpcMethodMap CJSONServiceDescription::m_methodMaps[] = {
55 { "JSONRPC.Introspect", CJSONRPC::Introspect },
56 { "JSONRPC.Version", CJSONRPC::Version },
57 { "JSONRPC.Permission", CJSONRPC::Permission },
58 { "JSONRPC.Ping", CJSONRPC::Ping },
59 { "JSONRPC.GetConfiguration", CJSONRPC::GetConfiguration },
60 { "JSONRPC.SetConfiguration", CJSONRPC::SetConfiguration },
61 { "JSONRPC.NotifyAll", CJSONRPC::NotifyAll },
64 { "Player.GetActivePlayers", CPlayerOperations::GetActivePlayers },
65 { "Player.GetProperties", CPlayerOperations::GetProperties },
66 { "Player.GetItem", CPlayerOperations::GetItem },
68 { "Player.PlayPause", CPlayerOperations::PlayPause },
69 { "Player.Stop", CPlayerOperations::Stop },
70 { "Player.SetSpeed", CPlayerOperations::SetSpeed },
71 { "Player.Seek", CPlayerOperations::Seek },
72 { "Player.Move", CPlayerOperations::Move },
73 { "Player.Zoom", CPlayerOperations::Zoom },
74 { "Player.Rotate", CPlayerOperations::Rotate },
76 { "Player.Open", CPlayerOperations::Open },
77 { "Player.GoTo", CPlayerOperations::GoTo },
78 { "Player.SetShuffle", CPlayerOperations::SetShuffle },
79 { "Player.SetRepeat", CPlayerOperations::SetRepeat },
80 { "Player.SetPartymode", CPlayerOperations::SetPartymode },
82 { "Player.SetAudioStream", CPlayerOperations::SetAudioStream },
83 { "Player.SetSubtitle", CPlayerOperations::SetSubtitle },
86 { "Playlist.GetPlaylists", CPlaylistOperations::GetPlaylists },
87 { "Playlist.GetProperties", CPlaylistOperations::GetProperties },
88 { "Playlist.GetItems", CPlaylistOperations::GetItems },
89 { "Playlist.Add", CPlaylistOperations::Add },
90 { "Playlist.Insert", CPlaylistOperations::Insert },
91 { "Playlist.Clear", CPlaylistOperations::Clear },
92 { "Playlist.Remove", CPlaylistOperations::Remove },
93 { "Playlist.Swap", CPlaylistOperations::Swap },
96 { "Files.GetSources", CFileOperations::GetRootDirectory },
97 { "Files.GetDirectory", CFileOperations::GetDirectory },
98 { "Files.GetFileDetails", CFileOperations::GetFileDetails },
99 { "Files.PrepareDownload", CFileOperations::PrepareDownload },
100 { "Files.Download", CFileOperations::Download },
103 { "AudioLibrary.GetArtists", CAudioLibrary::GetArtists },
104 { "AudioLibrary.GetArtistDetails", CAudioLibrary::GetArtistDetails },
105 { "AudioLibrary.GetAlbums", CAudioLibrary::GetAlbums },
106 { "AudioLibrary.GetAlbumDetails", CAudioLibrary::GetAlbumDetails },
107 { "AudioLibrary.GetSongs", CAudioLibrary::GetSongs },
108 { "AudioLibrary.GetSongDetails", CAudioLibrary::GetSongDetails },
109 { "AudioLibrary.GetRecentlyAddedAlbums", CAudioLibrary::GetRecentlyAddedAlbums },
110 { "AudioLibrary.GetRecentlyAddedSongs", CAudioLibrary::GetRecentlyAddedSongs },
111 { "AudioLibrary.GetRecentlyPlayedAlbums", CAudioLibrary::GetRecentlyPlayedAlbums },
112 { "AudioLibrary.GetRecentlyPlayedSongs", CAudioLibrary::GetRecentlyPlayedSongs },
113 { "AudioLibrary.GetGenres", CAudioLibrary::GetGenres },
114 { "AudioLibrary.SetArtistDetails", CAudioLibrary::SetArtistDetails },
115 { "AudioLibrary.SetAlbumDetails", CAudioLibrary::SetAlbumDetails },
116 { "AudioLibrary.SetSongDetails", CAudioLibrary::SetSongDetails },
117 { "AudioLibrary.Scan", CAudioLibrary::Scan },
118 { "AudioLibrary.Export", CAudioLibrary::Export },
119 { "AudioLibrary.Clean", CAudioLibrary::Clean },
122 { "VideoLibrary.GetGenres", CVideoLibrary::GetGenres },
123 { "VideoLibrary.GetMovies", CVideoLibrary::GetMovies },
124 { "VideoLibrary.GetMovieDetails", CVideoLibrary::GetMovieDetails },
125 { "VideoLibrary.GetMovieSets", CVideoLibrary::GetMovieSets },
126 { "VideoLibrary.GetMovieSetDetails", CVideoLibrary::GetMovieSetDetails },
127 { "VideoLibrary.GetTVShows", CVideoLibrary::GetTVShows },
128 { "VideoLibrary.GetTVShowDetails", CVideoLibrary::GetTVShowDetails },
129 { "VideoLibrary.GetSeasons", CVideoLibrary::GetSeasons },
130 { "VideoLibrary.GetSeasonDetails", CVideoLibrary::GetSeasonDetails },
131 { "VideoLibrary.GetEpisodes", CVideoLibrary::GetEpisodes },
132 { "VideoLibrary.GetEpisodeDetails", CVideoLibrary::GetEpisodeDetails },
133 { "VideoLibrary.GetMusicVideos", CVideoLibrary::GetMusicVideos },
134 { "VideoLibrary.GetMusicVideoDetails", CVideoLibrary::GetMusicVideoDetails },
135 { "VideoLibrary.GetRecentlyAddedMovies", CVideoLibrary::GetRecentlyAddedMovies },
136 { "VideoLibrary.GetRecentlyAddedEpisodes", CVideoLibrary::GetRecentlyAddedEpisodes },
137 { "VideoLibrary.GetRecentlyAddedMusicVideos", CVideoLibrary::GetRecentlyAddedMusicVideos },
138 { "VideoLibrary.SetMovieDetails", CVideoLibrary::SetMovieDetails },
139 { "VideoLibrary.SetMovieSetDetails", CVideoLibrary::SetMovieSetDetails },
140 { "VideoLibrary.SetTVShowDetails", CVideoLibrary::SetTVShowDetails },
141 { "VideoLibrary.SetSeasonDetails", CVideoLibrary::SetSeasonDetails },
142 { "VideoLibrary.SetEpisodeDetails", CVideoLibrary::SetEpisodeDetails },
143 { "VideoLibrary.SetMusicVideoDetails", CVideoLibrary::SetMusicVideoDetails },
144 { "VideoLibrary.RemoveMovie", CVideoLibrary::RemoveMovie },
145 { "VideoLibrary.RemoveTVShow", CVideoLibrary::RemoveTVShow },
146 { "VideoLibrary.RemoveEpisode", CVideoLibrary::RemoveEpisode },
147 { "VideoLibrary.RemoveMusicVideo", CVideoLibrary::RemoveMusicVideo },
148 { "VideoLibrary.Scan", CVideoLibrary::Scan },
149 { "VideoLibrary.Export", CVideoLibrary::Export },
150 { "VideoLibrary.Clean", CVideoLibrary::Clean },
153 { "Addons.GetAddons", CAddonsOperations::GetAddons },
154 { "Addons.GetAddonDetails", CAddonsOperations::GetAddonDetails },
155 { "Addons.SetAddonEnabled", CAddonsOperations::SetAddonEnabled },
156 { "Addons.ExecuteAddon", CAddonsOperations::ExecuteAddon },
159 { "GUI.GetProperties", CGUIOperations::GetProperties },
160 { "GUI.ActivateWindow", CGUIOperations::ActivateWindow },
161 { "GUI.ShowNotification", CGUIOperations::ShowNotification },
162 { "GUI.SetFullscreen", CGUIOperations::SetFullscreen },
163 { "GUI.SetStereoscopicMode", CGUIOperations::SetStereoscopicMode },
164 { "GUI.GetStereoscopicModes", CGUIOperations::GetStereoscopicModes },
167 { "PVR.GetProperties", CPVROperations::GetProperties },
168 { "PVR.GetChannelGroups", CPVROperations::GetChannelGroups },
169 { "PVR.GetChannelGroupDetails", CPVROperations::GetChannelGroupDetails },
170 { "PVR.GetChannels", CPVROperations::GetChannels },
171 { "PVR.GetChannelDetails", CPVROperations::GetChannelDetails },
172 { "PVR.GetBroadcasts", CPVROperations::GetBroadcasts },
173 { "PVR.GetBroadcastDetails", CPVROperations::GetBroadcastDetails },
174 { "PVR.Record", CPVROperations::Record },
175 { "PVR.Scan", CPVROperations::Scan },
177 // Profiles operations
178 { "Profiles.GetProfiles", CProfilesOperations::GetProfiles},
179 { "Profiles.GetCurrentProfile", CProfilesOperations::GetCurrentProfile},
180 { "Profiles.LoadProfile", CProfilesOperations::LoadProfile},
183 { "System.GetProperties", CSystemOperations::GetProperties },
184 { "System.EjectOpticalDrive", CSystemOperations::EjectOpticalDrive },
185 { "System.Shutdown", CSystemOperations::Shutdown },
186 { "System.Suspend", CSystemOperations::Suspend },
187 { "System.Hibernate", CSystemOperations::Hibernate },
188 { "System.Reboot", CSystemOperations::Reboot },
191 { "Input.SendText", CInputOperations::SendText },
192 { "Input.ExecuteAction", CInputOperations::ExecuteAction },
193 { "Input.Left", CInputOperations::Left },
194 { "Input.Right", CInputOperations::Right },
195 { "Input.Down", CInputOperations::Down },
196 { "Input.Up", CInputOperations::Up },
197 { "Input.Select", CInputOperations::Select },
198 { "Input.Back", CInputOperations::Back },
199 { "Input.ContextMenu", CInputOperations::ContextMenu },
200 { "Input.Info", CInputOperations::Info },
201 { "Input.Home", CInputOperations::Home },
202 { "Input.ShowCodec", CInputOperations::ShowCodec },
203 { "Input.ShowOSD", CInputOperations::ShowOSD },
205 // Application operations
206 { "Application.GetProperties", CApplicationOperations::GetProperties },
207 { "Application.SetVolume", CApplicationOperations::SetVolume },
208 { "Application.SetMute", CApplicationOperations::SetMute },
209 { "Application.Quit", CApplicationOperations::Quit },
211 // Favourites operations
212 { "Favourites.GetFavourites", CFavouritesOperations::GetFavourites },
213 { "Favourites.AddFavourite", CFavouritesOperations::AddFavourite },
215 // Textures operations
216 { "Textures.GetTextures", CTextureOperations::GetTextures },
217 { "Textures.RemoveTexture", CTextureOperations::RemoveTexture },
219 // Settings operations
220 { "Settings.GetSections", CSettingsOperations::GetSections },
221 { "Settings.GetCategories", CSettingsOperations::GetCategories },
222 { "Settings.GetSettings", CSettingsOperations::GetSettings },
223 { "Settings.GetSettingValue", CSettingsOperations::GetSettingValue },
224 { "Settings.SetSettingValue", CSettingsOperations::SetSettingValue },
225 { "Settings.ResetSettingValue", CSettingsOperations::ResetSettingValue },
228 { "XBMC.GetInfoLabels", CXBMCOperations::GetInfoLabels },
229 { "XBMC.GetInfoBooleans", CXBMCOperations::GetInfoBooleans }
232 JSONSchemaTypeDefinition::JSONSchemaTypeDefinition()
233 : missingReference(""), referencedTypeSet(false),
234 type(AnyValue), minimum(-std::numeric_limits<double>::max()), maximum(std::numeric_limits<double>::max()),
235 exclusiveMinimum(false), exclusiveMaximum(false), divisibleBy(0),
236 minLength(-1), maxLength(-1),
237 minItems(0), maxItems(0), uniqueItems(false),
238 hasAdditionalProperties(false)
241 bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /* = false */)
243 bool hasReference = false;
245 // Check if the type of the parameter defines a json reference
246 // to a type defined somewhere else
247 if (value.isMember("$ref") && value["$ref"].isString())
249 // Get the name of the referenced type
250 std::string refType = value["$ref"].asString();
251 // Check if the referenced type exists
252 JSONSchemaTypeDefinitionPtr referencedTypeDef = CJSONServiceDescription::GetType(refType);
253 if (refType.length() <= 0 || referencedTypeDef.get() == NULL)
255 CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s references an unknown type %s", name.c_str(), refType.c_str());
256 missingReference = refType;
260 std::string typeName = name;
261 *this = *referencedTypeDef;
262 if (!typeName.empty())
264 referencedType = referencedTypeDef;
267 else if (value.isMember("id") && value["id"].isString())
268 ID = GetString(value["id"], "");
270 // Check if the "required" field has been defined
271 optional = value.isMember("required") && value["required"].isBoolean() ? !value["required"].asBoolean() : true;
273 // Get the "description"
274 if (!hasReference || (value.isMember("description") && value["description"].isString()))
275 description = GetString(value["description"], "");
279 // If there is a specific default value, read it
280 if (value.isMember("default") && IsType(value["default"], type))
283 if (enums.size() <= 0)
285 // If the type has an enum definition we must make
286 // sure that the default value is a valid enum value
289 for (std::vector<CVariant>::const_iterator itr = enums.begin(); itr != enums.end(); ++itr)
291 if (value["default"] == *itr)
300 defaultValue = value["default"];
306 // Check whether this type extends an existing type
307 if (value.isMember("extends"))
309 if (value["extends"].isString())
311 std::string extendsName = GetString(value["extends"], "");
312 if (!extendsName.empty())
314 JSONSchemaTypeDefinitionPtr extendedTypeDef = CJSONServiceDescription::GetType(extendsName);
315 if (extendedTypeDef.get() == NULL)
317 CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", name.c_str(), extendsName.c_str());
318 missingReference = extendsName;
322 type = extendedTypeDef->type;
323 extends.push_back(extendedTypeDef);
326 else if (value["extends"].isArray())
328 JSONSchemaType extendedType = AnyValue;
329 for (unsigned int extendsIndex = 0; extendsIndex < value["extends"].size(); extendsIndex++)
331 std::string extendsName = GetString(value["extends"][extendsIndex], "");
332 if (!extendsName.empty())
334 JSONSchemaTypeDefinitionPtr extendedTypeDef = CJSONServiceDescription::GetType(extendsName);
335 if (extendedTypeDef.get() == NULL)
338 CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", name.c_str(), extendsName.c_str());
339 missingReference = extendsName;
343 if (extendsIndex == 0)
344 extendedType = extendedTypeDef->type;
345 else if (extendedType != extendedTypeDef->type)
348 CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends multiple JSON schema types of mismatching types", name.c_str());
352 extends.push_back(extendedTypeDef);
360 // Only read the "type" attribute if it's
361 // not an extending type
362 if (extends.size() <= 0)
364 // Get the defined type of the parameter
365 if (!CJSONServiceDescription::parseJSONSchemaType(value["type"], unionTypes, type, missingReference))
369 if (HasType(type, ObjectValue))
371 // If the type definition is of type "object"
372 // and has a "properties" definition we need
373 // to handle these as well
374 if (value.isMember("properties") && value["properties"].isObject())
376 // Get all child elements of the "properties"
377 // object and loop through them
378 for (CVariant::const_iterator_map itr = value["properties"].begin_map(); itr != value["properties"].end_map(); ++itr)
380 // Create a new type definition, store the name
381 // of the current property into it, parse it
382 // recursively and add its default value
383 // to the current type's default value
384 JSONSchemaTypeDefinitionPtr propertyType = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
385 propertyType->name = itr->first;
386 if (!propertyType->Parse(itr->second))
388 missingReference = propertyType->missingReference;
391 defaultValue[itr->first] = propertyType->defaultValue;
392 properties.add(propertyType);
396 hasAdditionalProperties = true;
397 additionalProperties = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
398 if (value.isMember("additionalProperties"))
400 if (value["additionalProperties"].isBoolean())
402 hasAdditionalProperties = value["additionalProperties"].asBoolean();
403 if (!hasAdditionalProperties)
405 additionalProperties.reset();
408 else if (value["additionalProperties"].isObject() && !value["additionalProperties"].isNull())
410 if (!additionalProperties->Parse(value["additionalProperties"]))
412 missingReference = additionalProperties->missingReference;
413 hasAdditionalProperties = false;
414 additionalProperties.reset();
416 CLog::Log(LOGDEBUG, "JSONRPC: Invalid additionalProperties schema definition in type %s", name.c_str());
422 CLog::Log(LOGDEBUG, "JSONRPC: Invalid additionalProperties definition in type %s", name.c_str());
428 // If the defined parameter is an array
429 // we need to check for detailed definitions
430 // of the array items
431 if (HasType(type, ArrayValue))
433 // Check for "uniqueItems" field
434 if (value.isMember("uniqueItems") && value["uniqueItems"].isBoolean())
435 uniqueItems = value["uniqueItems"].asBoolean();
439 // Check for "additionalItems" field
440 if (value.isMember("additionalItems"))
442 // If it is an object, there is only one schema for it
443 if (value["additionalItems"].isObject())
445 JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
446 if (additionalItem->Parse(value["additionalItems"]))
447 additionalItems.push_back(additionalItem);
450 CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" value for type %s", name.c_str());
451 missingReference = additionalItem->missingReference;
455 // If it is an array there may be multiple schema definitions
456 else if (value["additionalItems"].isArray())
458 for (unsigned int itemIndex = 0; itemIndex < value["additionalItems"].size(); itemIndex++)
460 JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
462 if (additionalItem->Parse(value["additionalItems"][itemIndex]))
463 additionalItems.push_back(additionalItem);
466 CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" value (item %d) for type %s", itemIndex, name.c_str());
467 missingReference = additionalItem->missingReference;
472 // If it is not a (array of) schema and not a bool (default value is false)
473 // it has an invalid value
474 else if (!value["additionalItems"].isBoolean())
476 CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" definition for type %s", name.c_str());
481 // If the "items" field is a single object
482 // we can parse that directly
483 if (value.isMember("items"))
485 if (value["items"].isObject())
487 JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
488 if (!item->Parse(value["items"]))
490 CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" for type %s", name.c_str());
491 missingReference = item->missingReference;
494 items.push_back(item);
496 // Otherwise if it is an array we need to
497 // parse all elements and store them
498 else if (value["items"].isArray())
500 for (CVariant::const_iterator_array itemItr = value["items"].begin_array(); itemItr != value["items"].end_array(); ++itemItr)
502 JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
503 if (!item->Parse(*itemItr))
505 CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" array for type %s", name.c_str());
506 missingReference = item->missingReference;
509 items.push_back(item);
514 minItems = (unsigned int)value["minItems"].asUnsignedInteger(0);
515 maxItems = (unsigned int)value["maxItems"].asUnsignedInteger(0);
518 if (HasType(type, NumberValue) || HasType(type, IntegerValue))
520 if ((type & NumberValue) == NumberValue)
522 minimum = value["minimum"].asDouble(-numeric_limits<double>::max());
523 maximum = value["maximum"].asDouble(numeric_limits<double>::max());
525 else if ((type & IntegerValue) == IntegerValue)
527 minimum = (double)value["minimum"].asInteger(numeric_limits<int>::min());
528 maximum = (double)value["maximum"].asInteger(numeric_limits<int>::max());
531 exclusiveMinimum = value["exclusiveMinimum"].asBoolean(false);
532 exclusiveMaximum = value["exclusiveMaximum"].asBoolean(false);
533 divisibleBy = (unsigned int)value["divisibleBy"].asUnsignedInteger(0);
536 if (HasType(type, StringValue))
538 minLength = (int)value["minLength"].asInteger(-1);
539 maxLength = (int)value["maxLength"].asInteger(-1);
542 // If the type definition is neither an
543 // "object" nor an "array" we can check
544 // for an "enum" definition
545 if (value.isMember("enum") && value["enum"].isArray())
547 // Loop through all elements in the "enum" array
548 for (CVariant::const_iterator_array enumItr = value["enum"].begin_array(); enumItr != value["enum"].end_array(); ++enumItr)
550 // Check for duplicates and eliminate them
551 bool approved = true;
552 for (unsigned int approvedIndex = 0; approvedIndex < enums.size(); approvedIndex++)
554 if (*enumItr == enums.at(approvedIndex))
561 // Only add the current item to the enum value
562 // list if it is not duplicate
564 enums.push_back(*enumItr);
568 if (type != ObjectValue)
570 // If there is a definition for a default value and its type
571 // matches the type of the parameter we can parse it
573 if (value.isMember("default") && IsType(value["default"], type))
575 if (enums.size() <= 0)
577 // If the type has an enum definition we must make
578 // sure that the default value is a valid enum value
581 for (std::vector<CVariant>::const_iterator itr = enums.begin(); itr != enums.end(); ++itr)
583 if (value["default"] == *itr)
593 defaultValue = value["default"];
596 // If the type of the default value definition does not
597 // match the type of the parameter we have to log this
598 if (value.isMember("default") && !IsType(value["default"], type))
599 CLog::Log(LOGDEBUG, "JSONRPC: Parameter %s has an invalid default value", name.c_str());
601 // If the type contains an "enum" we need to get the
602 // default value from the first enum value
603 if (enums.size() > 0)
604 defaultValue = enums.at(0);
605 // otherwise set a default value instead
607 SetDefaultValue(defaultValue, type);
614 JSONRPC_STATUS JSONSchemaTypeDefinition::Check(const CVariant &value, CVariant &outputValue, CVariant &errorData)
617 errorData["name"] = name;
618 SchemaValueTypeToJson(type, errorData["type"]);
619 CStdString errorMessage;
621 if (referencedType != NULL && !referencedTypeSet)
624 // Let's check the type of the provided parameter
625 if (!IsType(value, type))
627 errorMessage = StringUtils::Format("Invalid type %s received", ValueTypeToString(value.type()));
628 errorData["message"] = errorMessage.c_str();
629 return InvalidParams;
631 else if (value.isNull() && !HasType(type, NullValue))
633 errorData["message"] = "Received value is null";
634 return InvalidParams;
637 // Let's check if we have to handle a union type
638 if (unionTypes.size() > 0)
641 for (unsigned int unionIndex = 0; unionIndex < unionTypes.size(); unionIndex++)
644 CVariant testOutput = outputValue;
645 if (unionTypes.at(unionIndex)->Check(value, testOutput, dummyError) == OK)
648 outputValue = testOutput;
655 errorData["message"] = "Received value does not match any of the union type definitions";
656 return InvalidParams;
660 // First we need to check if this type extends another
661 // type and if so we need to check against the extended
663 if (extends.size() > 0)
665 for (unsigned int extendsIndex = 0; extendsIndex < extends.size(); extendsIndex++)
667 JSONRPC_STATUS status = extends.at(extendsIndex)->Check(value, outputValue, errorData);
671 CLog::Log(LOGDEBUG, "JSONRPC: Value does not match extended type %s of type %s", extends.at(extendsIndex)->ID.c_str(), name.c_str());
672 errorMessage = StringUtils::Format("value does not match extended type %s", extends.at(extendsIndex)->ID.c_str(), name.c_str());
673 errorData["message"] = errorMessage.c_str();
679 // If it is an array we need to
680 // - check the type of every element ("items")
681 // - check if they need to be unique ("uniqueItems")
682 if (HasType(type, ArrayValue) && value.isArray())
684 outputValue = CVariant(CVariant::VariantTypeArray);
685 // Check the number of items against minItems and maxItems
686 if ((minItems > 0 && value.size() < minItems) || (maxItems > 0 && value.size() > maxItems))
688 CLog::Log(LOGDEBUG, "JSONRPC: Number of array elements does not match minItems and/or maxItems in type %s", name.c_str());
689 if (minItems > 0 && maxItems > 0)
690 errorMessage = StringUtils::Format("Between %d and %d array items expected but %d received", minItems, maxItems, value.size());
691 else if (minItems > 0)
692 errorMessage = StringUtils::Format("At least %d array items expected but only %d received", minItems, value.size());
694 errorMessage = StringUtils::Format("Only %d array items expected but %d received", maxItems, value.size());
695 errorData["message"] = errorMessage.c_str();
696 return InvalidParams;
699 if (items.size() == 0)
701 else if (items.size() == 1)
703 JSONSchemaTypeDefinitionPtr itemType = items.at(0);
705 // Loop through all array elements
706 for (unsigned int arrayIndex = 0; arrayIndex < value.size(); arrayIndex++)
709 JSONRPC_STATUS status = itemType->Check(value[arrayIndex], temp, errorData["property"]);
710 outputValue.push_back(temp);
713 CLog::Log(LOGDEBUG, "JSONRPC: Array element at index %u does not match in type %s", arrayIndex, name.c_str());
714 errorMessage = StringUtils::Format("array element at index %u does not match", arrayIndex);
715 errorData["message"] = errorMessage.c_str();
720 // We have more than one element in "items"
721 // so we have tuple typing, which means that
722 // every element in the value array must match
723 // with the type at the same position in the
727 // If the number of elements in the value array
728 // does not match the number of elements in the
729 // "items" array and additional items are not
730 // allowed there is no need to check every element
731 if (value.size() < items.size() || (value.size() != items.size() && additionalItems.size() == 0))
733 CLog::Log(LOGDEBUG, "JSONRPC: One of the array elements does not match in type %s", name.c_str());
734 errorMessage = StringUtils::Format("%d array elements expected but %d received", items.size(), value.size());
735 errorData["message"] = errorMessage.c_str();
736 return InvalidParams;
739 // Loop through all array elements until there
740 // are either no more schemas in the "items"
741 // array or no more elements in the value's array
742 unsigned int arrayIndex;
743 for (arrayIndex = 0; arrayIndex < min(items.size(), (size_t)value.size()); arrayIndex++)
745 JSONRPC_STATUS status = items.at(arrayIndex)->Check(value[arrayIndex], outputValue[arrayIndex], errorData["property"]);
748 CLog::Log(LOGDEBUG, "JSONRPC: Array element at index %u does not match with items schema in type %s", arrayIndex, name.c_str());
753 if (additionalItems.size() > 0)
755 // Loop through the rest of the elements
756 // in the array and check them against the
758 for (; arrayIndex < value.size(); arrayIndex++)
761 for (unsigned int additionalIndex = 0; additionalIndex < additionalItems.size(); additionalIndex++)
764 if (additionalItems.at(additionalIndex)->Check(value[arrayIndex], outputValue[arrayIndex], dummyError) == OK)
773 CLog::Log(LOGDEBUG, "JSONRPC: Array contains non-conforming additional items in type %s", name.c_str());
774 errorMessage = StringUtils::Format("Array element at index %u does not match the \"additionalItems\" schema", arrayIndex);
775 errorData["message"] = errorMessage.c_str();
776 return InvalidParams;
782 // If every array element is unique we need to check each one
785 for (unsigned int checkingIndex = 0; checkingIndex < outputValue.size(); checkingIndex++)
787 for (unsigned int checkedIndex = checkingIndex + 1; checkedIndex < outputValue.size(); checkedIndex++)
789 // If two elements are the same they are not unique
790 if (outputValue[checkingIndex] == outputValue[checkedIndex])
792 CLog::Log(LOGDEBUG, "JSONRPC: Not unique array element at index %u and %u in type %s", checkingIndex, checkedIndex, name.c_str());
793 errorMessage = StringUtils::Format("Array element at index %u is not unique (same as array element at index %u)", checkingIndex, checkedIndex);
794 errorData["message"] = errorMessage.c_str();
795 return InvalidParams;
804 // If it is an object we need to check every element
805 // against the defined "properties"
806 if (HasType(type, ObjectValue) && value.isObject())
808 unsigned int handled = 0;
809 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesEnd = properties.end();
810 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesIterator;
811 for (propertiesIterator = properties.begin(); propertiesIterator != propertiesEnd; ++propertiesIterator)
813 if (value.isMember(propertiesIterator->second->name))
815 JSONRPC_STATUS status = propertiesIterator->second->Check(value[propertiesIterator->second->name], outputValue[propertiesIterator->second->name], errorData["property"]);
818 CLog::Log(LOGDEBUG, "JSONRPC: Invalid property \"%s\" in type %s", propertiesIterator->second->name.c_str(), name.c_str());
823 else if (propertiesIterator->second->optional)
824 outputValue[propertiesIterator->second->name] = propertiesIterator->second->defaultValue;
827 errorData["property"]["name"] = propertiesIterator->second->name.c_str();
828 errorData["property"]["type"] = SchemaValueTypeToString(propertiesIterator->second->type);
829 errorData["message"] = "Missing property";
830 return InvalidParams;
834 // Additional properties are not allowed
835 if (handled < value.size())
837 // If additional properties are allowed we need to check if
838 // they match the defined schema
839 if (hasAdditionalProperties && additionalProperties != NULL)
841 CVariant::const_iterator_map iter;
842 CVariant::const_iterator_map iterEnd = value.end_map();
843 for (iter = value.begin_map(); iter != iterEnd; iter++)
845 if (properties.find(iter->first) != properties.end())
848 // If the additional property is of type "any"
849 // we can simply copy its value to the output
851 if (additionalProperties->type == AnyValue)
853 outputValue[iter->first] = value[iter->first];
857 JSONRPC_STATUS status = additionalProperties->Check(value[iter->first], outputValue[iter->first], errorData["property"]);
860 CLog::Log(LOGDEBUG, "JSONRPC: Invalid additional property \"%s\" in type %s", iter->first.c_str(), name.c_str());
865 // If we still have unchecked properties but additional
866 // properties are not allowed, we have invalid parameters
867 else if (!hasAdditionalProperties || additionalProperties == NULL)
869 errorData["message"] = "Unexpected additional properties received";
870 errorData.erase("property");
871 return InvalidParams;
878 // It's neither an array nor an object
880 // If it can only take certain values ("enum")
881 // we need to check against those
882 if (enums.size() > 0)
885 for (std::vector<CVariant>::const_iterator enumItr = enums.begin(); enumItr != enums.end(); ++enumItr)
887 if (*enumItr == value)
896 CLog::Log(LOGDEBUG, "JSONRPC: Value does not match any of the enum values in type %s", name.c_str());
897 errorData["message"] = "Received value does not match any of the defined enum values";
898 return InvalidParams;
902 // If we have a number or an integer type, we need
903 // to check the minimum and maximum values
904 if ((HasType(type, NumberValue) && value.isDouble()) || (HasType(type, IntegerValue) && value.isInteger()))
907 if (value.isDouble())
908 numberValue = value.asDouble();
910 numberValue = (double)value.asInteger();
912 if ((exclusiveMinimum && numberValue <= minimum) || (!exclusiveMinimum && numberValue < minimum) ||
914 (exclusiveMaximum && numberValue >= maximum) || (!exclusiveMaximum && numberValue > maximum))
916 CLog::Log(LOGDEBUG, "JSONRPC: Value does not lay between minimum and maximum in type %s", name.c_str());
917 if (value.isDouble())
918 errorMessage = StringUtils::Format("Value between %f (%s) and %f (%s) expected but %f received",
919 minimum, exclusiveMinimum ? "exclusive" : "inclusive", maximum, exclusiveMaximum ? "exclusive" : "inclusive", numberValue);
921 errorMessage = StringUtils::Format("Value between %d (%s) and %d (%s) expected but %d received",
922 (int)minimum, exclusiveMinimum ? "exclusive" : "inclusive", (int)maximum, exclusiveMaximum ? "exclusive" : "inclusive", (int)numberValue);
923 errorData["message"] = errorMessage.c_str();
924 return InvalidParams;
927 if ((HasType(type, IntegerValue) && divisibleBy > 0 && ((int)numberValue % divisibleBy) != 0))
929 CLog::Log(LOGDEBUG, "JSONRPC: Value does not meet divisibleBy requirements in type %s", name.c_str());
930 errorMessage = StringUtils::Format("Value should be divisible by %d but %d received", divisibleBy, (int)numberValue);
931 errorData["message"] = errorMessage.c_str();
932 return InvalidParams;
936 // If we have a string, we need to check the length
937 if (HasType(type, StringValue) && value.isString())
939 int size = value.asString().size();
940 if (size < minLength)
942 CLog::Log(LOGDEBUG, "JSONRPC: Value does not meet minLength requirements in type %s", name.c_str());
943 errorMessage = StringUtils::Format("Value should have a minimum length of %d but has a length of %d", minLength, size);
944 errorData["message"] = errorMessage.c_str();
945 return InvalidParams;
948 if (maxLength >= 0 && size > maxLength)
950 CLog::Log(LOGDEBUG, "JSONRPC: Value does not meet maxLength requirements in type %s", name.c_str());
951 errorMessage = StringUtils::Format("Value should have a maximum length of %d but has a length of %d", maxLength, size);
952 errorData["message"] = errorMessage.c_str();
953 return InvalidParams;
957 // Otherwise it can have any value
962 void JSONSchemaTypeDefinition::Print(bool isParameter, bool isGlobal, bool printDefault, bool printDescriptions, CVariant &output) const
964 bool typeReference = false;
966 // Printing general fields
968 output["name"] = name;
972 else if (!ID.empty())
975 typeReference = true;
978 if (printDescriptions && !description.empty())
979 output["description"] = description;
981 if (isParameter || printDefault)
984 output["required"] = true;
985 if (optional && type != ObjectValue && type != ArrayValue)
986 output["default"] = defaultValue;
991 if (extends.size() == 1)
993 output["extends"] = extends.at(0)->ID;
995 else if (extends.size() > 1)
997 output["extends"] = CVariant(CVariant::VariantTypeArray);
998 for (unsigned int extendsIndex = 0; extendsIndex < extends.size(); extendsIndex++)
999 output["extends"].append(extends.at(extendsIndex)->ID);
1001 else if (unionTypes.size() > 0)
1003 output["type"] = CVariant(CVariant::VariantTypeArray);
1004 for (unsigned int unionIndex = 0; unionIndex < unionTypes.size(); unionIndex++)
1006 CVariant unionOutput = CVariant(CVariant::VariantTypeObject);
1007 unionTypes.at(unionIndex)->Print(false, false, false, printDescriptions, unionOutput);
1008 output["type"].append(unionOutput);
1012 CJSONUtils::SchemaValueTypeToJson(type, output["type"]);
1014 // Printing enum field
1015 if (enums.size() > 0)
1017 output["enums"] = CVariant(CVariant::VariantTypeArray);
1018 for (unsigned int enumIndex = 0; enumIndex < enums.size(); enumIndex++)
1019 output["enums"].append(enums.at(enumIndex));
1022 // Printing integer/number fields
1023 if (CJSONUtils::HasType(type, IntegerValue) || CJSONUtils::HasType(type, NumberValue))
1025 if (CJSONUtils::HasType(type, NumberValue))
1027 if (minimum > -numeric_limits<double>::max())
1028 output["minimum"] = minimum;
1029 if (maximum < numeric_limits<double>::max())
1030 output["maximum"] = maximum;
1034 if (minimum > numeric_limits<int>::min())
1035 output["minimum"] = (int)minimum;
1036 if (maximum < numeric_limits<int>::max())
1037 output["maximum"] = (int)maximum;
1040 if (exclusiveMinimum)
1041 output["exclusiveMinimum"] = true;
1042 if (exclusiveMaximum)
1043 output["exclusiveMaximum"] = true;
1044 if (divisibleBy > 0)
1045 output["divisibleBy"] = divisibleBy;
1047 if (CJSONUtils::HasType(type, StringValue))
1050 output["minLength"] = minLength;
1052 output["maxLength"] = maxLength;
1055 // Print array fields
1056 if (CJSONUtils::HasType(type, ArrayValue))
1058 if (items.size() == 1)
1060 items.at(0)->Print(false, false, false, printDescriptions, output["items"]);
1062 else if (items.size() > 1)
1064 output["items"] = CVariant(CVariant::VariantTypeArray);
1065 for (unsigned int itemIndex = 0; itemIndex < items.size(); itemIndex++)
1067 CVariant item = CVariant(CVariant::VariantTypeObject);
1068 items.at(itemIndex)->Print(false, false, false, printDescriptions, item);
1069 output["items"].append(item);
1074 output["minItems"] = minItems;
1076 output["maxItems"] = maxItems;
1078 if (additionalItems.size() == 1)
1080 additionalItems.at(0)->Print(false, false, false, printDescriptions, output["additionalItems"]);
1082 else if (additionalItems.size() > 1)
1084 output["additionalItems"] = CVariant(CVariant::VariantTypeArray);
1085 for (unsigned int addItemIndex = 0; addItemIndex < additionalItems.size(); addItemIndex++)
1087 CVariant item = CVariant(CVariant::VariantTypeObject);
1088 additionalItems.at(addItemIndex)->Print(false, false, false, printDescriptions, item);
1089 output["additionalItems"].append(item);
1094 output["uniqueItems"] = true;
1097 // Print object fields
1098 if (CJSONUtils::HasType(type, ObjectValue))
1100 if (properties.size() > 0)
1102 output["properties"] = CVariant(CVariant::VariantTypeObject);
1104 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesEnd = properties.end();
1105 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesIterator;
1106 for (propertiesIterator = properties.begin(); propertiesIterator != propertiesEnd; ++propertiesIterator)
1108 propertiesIterator->second->Print(false, false, true, printDescriptions, output["properties"][propertiesIterator->first]);
1112 if (!hasAdditionalProperties)
1113 output["additionalProperties"] = false;
1114 else if (additionalProperties != NULL && additionalProperties->type != AnyValue)
1115 additionalProperties->Print(false, false, true, printDescriptions, output["additionalProperties"]);
1120 void JSONSchemaTypeDefinition::Set(const JSONSchemaTypeDefinitionPtr typeDefinition)
1122 if (typeDefinition.get() == NULL)
1125 string origName = name;
1126 string origDescription = description;
1127 bool origOptional = optional;
1128 CVariant origDefaultValue = defaultValue;
1129 JSONSchemaTypeDefinitionPtr referencedTypeDef = referencedType;
1131 // set all the values from the given type definition
1132 *this = *typeDefinition;
1134 // restore the original values
1135 if (!origName.empty())
1138 if (!origDescription.empty())
1139 description = origDescription;
1142 optional = origOptional;
1144 if (!origDefaultValue.isNull())
1145 defaultValue = origDefaultValue;
1147 if (referencedTypeDef.get() != NULL)
1148 referencedType = referencedTypeDef;
1150 referencedTypeSet = true;
1153 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::CJsonSchemaPropertiesMap()
1155 m_propertiesmap = std::map<std::string, JSONSchemaTypeDefinitionPtr>();
1158 void JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::add(JSONSchemaTypeDefinitionPtr property)
1160 CStdString name = property->name;
1161 name = name.ToLower();
1162 m_propertiesmap[name] = property;
1165 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::begin() const
1167 return m_propertiesmap.begin();
1170 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::find(const std::string& key) const
1172 return m_propertiesmap.find(key);
1175 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::end() const
1177 return m_propertiesmap.end();
1180 unsigned int JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::size() const
1182 return m_propertiesmap.size();
1185 JsonRpcMethod::JsonRpcMethod()
1186 : missingReference(""), method(NULL),
1187 returns(new JSONSchemaTypeDefinition())
1190 bool JsonRpcMethod::Parse(const CVariant &value)
1192 // Parse XBMC specific information about the method
1193 if (value.isMember("transport") && value["transport"].isArray())
1196 for (unsigned int index = 0; index < value["transport"].size(); index++)
1197 transport |= StringToTransportLayer(value["transport"][index].asString());
1199 transportneed = (TransportLayerCapability)transport;
1202 transportneed = StringToTransportLayer(value.isMember("transport") ? value["transport"].asString() : "");
1204 if (value.isMember("permission") && value["permission"].isArray())
1206 int permissions = 0;
1207 for (unsigned int index = 0; index < value["permission"].size(); index++)
1208 permissions |= StringToPermission(value["permission"][index].asString());
1210 permission = (OperationPermission)permissions;
1213 permission = StringToPermission(value.isMember("permission") ? value["permission"].asString() : "");
1215 description = GetString(value["description"], "");
1217 // Check whether there are parameters defined
1218 if (value.isMember("params") && value["params"].isArray())
1220 // Loop through all defined parameters
1221 for (unsigned int paramIndex = 0; paramIndex < value["params"].size(); paramIndex++)
1223 CVariant parameter = value["params"][paramIndex];
1224 // If the parameter definition does not contain a valid "name" or
1225 // "type" element we will ignore it
1226 if (!parameter.isMember("name") || !parameter["name"].isString() ||
1227 (!parameter.isMember("type") && !parameter.isMember("$ref") && !parameter.isMember("extends")) ||
1228 (parameter.isMember("type") && !parameter["type"].isString() && !parameter["type"].isArray()) ||
1229 (parameter.isMember("$ref") && !parameter["$ref"].isString()) ||
1230 (parameter.isMember("extends") && !parameter["extends"].isString() && !parameter["extends"].isArray()))
1232 CLog::Log(LOGDEBUG, "JSONRPC: Method %s has a badly defined parameter", name.c_str());
1236 // Parse the parameter and add it to the list
1237 // of defined parameters
1238 JSONSchemaTypeDefinitionPtr param = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1239 if (!parseParameter(parameter, param))
1241 missingReference = param->missingReference;
1244 parameters.push_back(param);
1248 // Parse the return value of the method
1249 if (!parseReturn(value))
1251 missingReference = returns->missingReference;
1258 JSONRPC_STATUS JsonRpcMethod::Check(const CVariant &requestParameters, ITransportLayer *transport, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters) const
1260 if (transport != NULL && (transport->GetCapabilities() & transportneed) == transportneed)
1262 if (client != NULL && (client->GetPermissionFlags() & permission) == permission && (!notification || (permission & OPERATION_PERMISSION_NOTIFICATION) == permission))
1264 methodCall = method;
1266 // Count the number of actually handled (present)
1268 unsigned int handled = 0;
1269 CVariant errorData = CVariant(CVariant::VariantTypeObject);
1270 errorData["method"] = name;
1272 // Loop through all the parameters to check
1273 for (unsigned int i = 0; i < parameters.size(); i++)
1275 // Evaluate the current parameter
1276 JSONRPC_STATUS status = checkParameter(requestParameters, parameters.at(i), i, outputParameters, handled, errorData);
1279 // Return the error data object in the outputParameters reference
1280 outputParameters = errorData;
1285 // Check if there were unnecessary parameters
1286 if (handled < requestParameters.size())
1288 errorData["message"] = "Too many parameters";
1289 outputParameters = errorData;
1290 return InvalidParams;
1296 return BadPermission;
1299 return MethodNotFound;
1302 bool JsonRpcMethod::parseParameter(const CVariant &value, JSONSchemaTypeDefinitionPtr parameter)
1304 parameter->name = GetString(value["name"], "");
1306 // Parse the type and default value of the parameter
1307 return parameter->Parse(value, true);
1310 bool JsonRpcMethod::parseReturn(const CVariant &value)
1312 // Only parse the "returns" definition if there is one
1313 if (!value.isMember("returns"))
1315 returns->type = NullValue;
1319 // If the type of the return value is defined as a simple string we can parse it directly
1320 if (value["returns"].isString())
1321 return CJSONServiceDescription::parseJSONSchemaType(value["returns"], returns->unionTypes, returns->type, missingReference);
1323 // otherwise we have to parse the whole type definition
1324 if (!returns->Parse(value["returns"]))
1326 missingReference = returns->missingReference;
1333 JSONRPC_STATUS JsonRpcMethod::checkParameter(const CVariant &requestParameters, JSONSchemaTypeDefinitionPtr type, unsigned int position, CVariant &outputParameters, unsigned int &handled, CVariant &errorData)
1335 // Let's check if the parameter has been provided
1336 if (ParameterExists(requestParameters, type->name, position))
1338 // Get the parameter
1339 CVariant parameterValue = GetParameter(requestParameters, type->name, position);
1341 // Evaluate the type of the parameter
1342 JSONRPC_STATUS status = type->Check(parameterValue, outputParameters[type->name], errorData["stack"]);
1346 // The parameter was present and valid
1349 // If the parameter has not been provided but is optional
1350 // we can use its default value
1351 else if (type->optional)
1352 outputParameters[type->name] = type->defaultValue;
1353 // The parameter is required but has not been provided => invalid
1356 errorData["stack"]["name"] = type->name;
1357 SchemaValueTypeToJson(type->type, errorData["stack"]["type"]);
1358 errorData["stack"]["message"] = "Missing parameter";
1359 return InvalidParams;
1365 void CJSONServiceDescription::Cleanup()
1367 // reset all of the static data
1368 m_notifications.clear();
1369 m_actionMap.clear();
1371 m_incompleteDefinitions.clear();
1374 bool CJSONServiceDescription::prepareDescription(std::string &description, CVariant &descriptionObject, std::string &name)
1376 if (description.empty())
1378 CLog::Log(LOGERROR, "JSONRPC: Missing JSON Schema definition for \"%s\"", name.c_str());
1382 if (description.at(0) != '{')
1384 description = StringUtils::Format("{%s}", description.c_str());
1387 descriptionObject = CJSONVariantParser::Parse((const unsigned char *)description.c_str(), description.size());
1389 // Make sure the method description actually exists and represents an object
1390 if (!descriptionObject.isObject())
1392 CLog::Log(LOGERROR, "JSONRPC: Unable to parse JSON Schema definition for \"%s\"", name.c_str());
1396 CVariant::const_iterator_map member = descriptionObject.begin_map();
1397 if (member != descriptionObject.end_map())
1398 name = member->first;
1401 (!descriptionObject[name].isMember("type") && !descriptionObject[name].isMember("$ref") && !descriptionObject[name].isMember("extends")))
1403 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for \"%s\"", name.c_str());
1410 bool CJSONServiceDescription::addMethod(const std::string &jsonMethod, MethodCall method)
1412 CVariant descriptionObject;
1413 std::string methodName;
1415 std::string modJsonMethod = jsonMethod;
1416 // Make sure the method description actually exists and represents an object
1417 if (!prepareDescription(modJsonMethod, descriptionObject, methodName))
1419 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for method \"%s\"", methodName.c_str());
1423 if (m_actionMap.find(methodName) != m_actionMap.end())
1425 CLog::Log(LOGERROR, "JSONRPC: There already is a method with the name \"%s\"", methodName.c_str());
1429 std::string type = GetString(descriptionObject[methodName]["type"], "");
1430 if (type.compare("method") != 0)
1432 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for method \"%s\"", methodName.c_str());
1438 unsigned int size = sizeof(m_methodMaps) / sizeof(JsonRpcMethodMap);
1439 for (unsigned int index = 0; index < size; index++)
1441 if (methodName.compare(m_methodMaps[index].name) == 0)
1443 method = m_methodMaps[index].method;
1450 CLog::Log(LOGERROR, "JSONRPC: Missing implementation for method \"%s\"", methodName.c_str());
1455 // Parse the details of the method
1456 JsonRpcMethod newMethod;
1457 newMethod.name = methodName;
1458 newMethod.method = method;
1460 if (!newMethod.Parse(descriptionObject[newMethod.name]))
1462 CLog::Log(LOGERROR, "JSONRPC: Could not parse method \"%s\"", methodName.c_str());
1463 if (!newMethod.missingReference.empty())
1465 IncompleteSchemaDefinition incomplete;
1466 incomplete.Schema = modJsonMethod;
1467 incomplete.Type = SchemaDefinitionMethod;
1468 incomplete.Method = method;
1470 IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(newMethod.missingReference);
1471 if (iter == m_incompleteDefinitions.end())
1472 m_incompleteDefinitions[newMethod.missingReference] = std::vector<IncompleteSchemaDefinition>();
1474 CLog::Log(LOGINFO, "JSONRPC: Adding method \"%s\" to list of incomplete definitions (waiting for \"%s\")", methodName.c_str(), newMethod.missingReference.c_str());
1475 m_incompleteDefinitions[newMethod.missingReference].push_back(incomplete);
1481 m_actionMap.add(newMethod);
1486 bool CJSONServiceDescription::AddType(const std::string &jsonType)
1488 CVariant descriptionObject;
1489 std::string typeName;
1491 std::string modJsonType = jsonType;
1492 if (!prepareDescription(modJsonType, descriptionObject, typeName))
1494 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for type \"%s\"", typeName.c_str());
1498 if (m_types.find(typeName) != m_types.end())
1500 CLog::Log(LOGERROR, "JSONRPC: There already is a type with the name \"%s\"", typeName.c_str());
1504 // Make sure the "id" attribute is correctly populated
1505 descriptionObject[typeName]["id"] = typeName;
1507 JSONSchemaTypeDefinitionPtr globalType = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1508 globalType->name = typeName;
1509 globalType->ID = typeName;
1510 CJSONServiceDescription::addReferenceTypeDefinition(globalType);
1512 if (!globalType->Parse(descriptionObject[typeName]))
1514 CLog::Log(LOGERROR, "JSONRPC: Could not parse type \"%s\"", typeName.c_str());
1515 CJSONServiceDescription::removeReferenceTypeDefinition(typeName);
1516 if (!globalType->missingReference.empty())
1518 IncompleteSchemaDefinition incomplete;
1519 incomplete.Schema = modJsonType;
1520 incomplete.Type = SchemaDefinitionType;
1522 IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(globalType->missingReference);
1523 if (iter == m_incompleteDefinitions.end())
1524 m_incompleteDefinitions[globalType->missingReference] = std::vector<IncompleteSchemaDefinition>();
1526 CLog::Log(LOGINFO, "JSONRPC: Adding type \"%s\" to list of incomplete definitions (waiting for \"%s\")", typeName.c_str(), globalType->missingReference.c_str());
1527 m_incompleteDefinitions[globalType->missingReference].push_back(incomplete);
1538 bool CJSONServiceDescription::AddMethod(const std::string &jsonMethod, MethodCall method)
1542 CLog::Log(LOGERROR, "JSONRPC: Invalid JSONRPC method implementation");
1546 return addMethod(jsonMethod, method);
1549 bool CJSONServiceDescription::AddBuiltinMethod(const std::string &jsonMethod)
1551 return addMethod(jsonMethod, NULL);
1554 bool CJSONServiceDescription::AddNotification(const std::string &jsonNotification)
1556 CVariant descriptionObject;
1557 std::string notificationName;
1559 std::string modJsonNotification = jsonNotification;
1560 // Make sure the notification description actually exists and represents an object
1561 if (!prepareDescription(modJsonNotification, descriptionObject, notificationName))
1563 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for notification \"%s\"", notificationName.c_str());
1567 if (m_notifications.find(notificationName) != m_notifications.end())
1569 CLog::Log(LOGERROR, "JSONRPC: There already is a notification with the name \"%s\"", notificationName.c_str());
1573 std::string type = GetString(descriptionObject[notificationName]["type"], "");
1574 if (type.compare("notification") != 0)
1576 CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for notification \"%s\"", notificationName.c_str());
1580 m_notifications[notificationName] = descriptionObject;
1585 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<CVariant> &values, CVariant::VariantType type /* = CVariant::VariantTypeNull */, const CVariant &defaultValue /* = CVariant::ConstNullVariant */)
1587 if (name.empty() || m_types.find(name) != m_types.end() ||
1591 JSONSchemaTypeDefinitionPtr definition = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1592 definition->ID = name;
1594 std::vector<CVariant::VariantType> types;
1595 bool autoType = false;
1596 if (type == CVariant::VariantTypeNull)
1599 types.push_back(type);
1601 for (unsigned int index = 0; index < values.size(); index++)
1604 types.push_back(values[index].type());
1605 else if (type != CVariant::VariantTypeConstNull && type != values[index].type())
1608 definition->enums.insert(definition->enums.begin(), values.begin(), values.end());
1610 int schemaType = (int)AnyValue;
1611 for (unsigned int index = 0; index < types.size(); index++)
1613 JSONSchemaType currentType;
1616 case CVariant::VariantTypeString:
1617 currentType = StringValue;
1619 case CVariant::VariantTypeDouble:
1620 currentType = NumberValue;
1622 case CVariant::VariantTypeInteger:
1623 case CVariant::VariantTypeUnsignedInteger:
1624 currentType = IntegerValue;
1626 case CVariant::VariantTypeBoolean:
1627 currentType = BooleanValue;
1629 case CVariant::VariantTypeArray:
1630 currentType = ArrayValue;
1632 case CVariant::VariantTypeObject:
1633 currentType = ObjectValue;
1635 case CVariant::VariantTypeConstNull:
1636 currentType = AnyValue;
1639 case CVariant::VariantTypeNull:
1644 schemaType = currentType;
1646 schemaType |= (int)currentType;
1648 definition->type = (JSONSchemaType)schemaType;
1650 if (defaultValue.type() == CVariant::VariantTypeConstNull)
1651 definition->defaultValue = definition->enums.at(0);
1653 definition->defaultValue = defaultValue;
1655 addReferenceTypeDefinition(definition);
1660 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<std::string> &values)
1662 std::vector<CVariant> enums;
1663 for (std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); ++it)
1664 enums.push_back(CVariant(*it));
1666 return AddEnum(name, enums, CVariant::VariantTypeString);
1669 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<int> &values)
1671 std::vector<CVariant> enums;
1672 for (std::vector<int>::const_iterator it = values.begin(); it != values.end(); ++it)
1673 enums.push_back(CVariant(*it));
1675 return AddEnum(name, enums, CVariant::VariantTypeInteger);
1678 const char* CJSONServiceDescription::GetVersion()
1680 return JSONRPC_SERVICE_VERSION;
1683 JSONRPC_STATUS CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport, IClient *client,
1684 bool printDescriptions /* = true */, bool printMetadata /* = false */, bool filterByTransport /* = true */,
1685 const std::string &filterByName /* = "" */, const std::string &filterByType /* = "" */, bool printReferences /* = true */)
1687 std::map<std::string, JSONSchemaTypeDefinitionPtr> types;
1688 CJsonRpcMethodMap methods;
1689 std::map<std::string, CVariant> notifications;
1691 int clientPermissions = client->GetPermissionFlags();
1692 int transportCapabilities = transport->GetCapabilities();
1694 if (filterByName.size() > 0)
1696 CStdString name = filterByName;
1698 if (filterByType == "method")
1700 name = name.ToLower();
1702 CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator = m_actionMap.find(name);
1703 if (methodIterator != m_actionMap.end() &&
1704 (clientPermissions & methodIterator->second.permission) == methodIterator->second.permission && ((transportCapabilities & methodIterator->second.transportneed) == methodIterator->second.transportneed || !filterByTransport))
1705 methods.add(methodIterator->second);
1707 return InvalidParams;
1709 else if (filterByType == "namespace")
1711 // append a . delimiter to make sure we check for a namespace
1712 name = name.ToLower().append(".");
1714 CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
1715 CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = m_actionMap.end();
1716 for (methodIterator = m_actionMap.begin(); methodIterator != methodIteratorEnd; methodIterator++)
1718 // Check if the given name is at the very beginning of the method name
1719 if (methodIterator->first.find(name) == 0 &&
1720 (clientPermissions & methodIterator->second.permission) == methodIterator->second.permission && ((transportCapabilities & methodIterator->second.transportneed) == methodIterator->second.transportneed || !filterByTransport))
1721 methods.add(methodIterator->second);
1724 if (methods.begin() == methods.end())
1725 return InvalidParams;
1727 else if (filterByType == "type")
1729 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIterator = m_types.find(name);
1730 if (typeIterator != m_types.end())
1731 types[typeIterator->first] = typeIterator->second;
1733 return InvalidParams;
1735 else if (filterByType == "notification")
1737 std::map<std::string, CVariant>::const_iterator notificationIterator = m_notifications.find(name);
1738 if (notificationIterator != m_notifications.end())
1739 notifications[notificationIterator->first] = notificationIterator->second;
1741 return InvalidParams;
1744 return InvalidParams;
1746 // If we need to print all referenced types we have to go through all parameters etc
1747 if (printReferences)
1749 std::vector<std::string> referencedTypes;
1751 // Loop through all printed types to get all referenced types
1752 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIterator;
1753 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIteratorEnd = types.end();
1754 for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; ++typeIterator)
1755 getReferencedTypes(typeIterator->second, referencedTypes);
1757 // Loop through all printed method's parameters and return value to get all referenced types
1758 CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
1759 CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end();
1760 for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++)
1762 for (unsigned int index = 0; index < methodIterator->second.parameters.size(); index++)
1763 getReferencedTypes(methodIterator->second.parameters.at(index), referencedTypes);
1765 getReferencedTypes(methodIterator->second.returns, referencedTypes);
1768 for (unsigned int index = 0; index < referencedTypes.size(); index++)
1770 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIterator = m_types.find(referencedTypes.at(index));
1771 if (typeIterator != m_types.end())
1772 types[typeIterator->first] = typeIterator->second;
1779 methods = m_actionMap;
1780 notifications = m_notifications;
1784 result["id"] = JSONRPC_SERVICE_ID;
1785 result["version"] = JSONRPC_SERVICE_VERSION;
1786 result["description"] = JSONRPC_SERVICE_DESCRIPTION;
1788 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIterator;
1789 std::map<std::string, JSONSchemaTypeDefinitionPtr>::const_iterator typeIteratorEnd = types.end();
1790 for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; ++typeIterator)
1792 CVariant currentType = CVariant(CVariant::VariantTypeObject);
1793 typeIterator->second->Print(false, true, true, printDescriptions, currentType);
1795 result["types"][typeIterator->first] = currentType;
1798 // Iterate through all json rpc methods
1799 CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
1800 CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end();
1801 for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++)
1803 if ((clientPermissions & methodIterator->second.permission) != methodIterator->second.permission || ((transportCapabilities & methodIterator->second.transportneed) != methodIterator->second.transportneed && filterByTransport))
1806 CVariant currentMethod = CVariant(CVariant::VariantTypeObject);
1808 currentMethod["type"] = "method";
1809 if (printDescriptions && !methodIterator->second.description.empty())
1810 currentMethod["description"] = methodIterator->second.description;
1813 CVariant permissions(CVariant::VariantTypeArray);
1814 for (int i = ReadData; i <= OPERATION_PERMISSION_ALL; i *= 2)
1816 if ((methodIterator->second.permission & i) == i)
1817 permissions.push_back(PermissionToString((OperationPermission)i));
1820 if (permissions.size() == 1)
1821 currentMethod["permission"] = permissions[0];
1823 currentMethod["permission"] = permissions;
1826 currentMethod["params"] = CVariant(CVariant::VariantTypeArray);
1827 for (unsigned int paramIndex = 0; paramIndex < methodIterator->second.parameters.size(); paramIndex++)
1829 CVariant param = CVariant(CVariant::VariantTypeObject);
1830 methodIterator->second.parameters.at(paramIndex)->Print(true, false, true, printDescriptions, param);
1831 currentMethod["params"].append(param);
1834 methodIterator->second.returns->Print(false, false, false, printDescriptions, currentMethod["returns"]);
1836 result["methods"][methodIterator->second.name] = currentMethod;
1839 // Print notification description
1840 std::map<std::string, CVariant>::const_iterator notificationIterator;
1841 std::map<std::string, CVariant>::const_iterator notificationIteratorEnd = notifications.end();
1842 for (notificationIterator = notifications.begin(); notificationIterator != notificationIteratorEnd; ++notificationIterator)
1843 result["notifications"][notificationIterator->first] = notificationIterator->second[notificationIterator->first];
1848 JSONRPC_STATUS CJSONServiceDescription::CheckCall(const char* const method, const CVariant &requestParameters, ITransportLayer *transport, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters)
1850 CJsonRpcMethodMap::JsonRpcMethodIterator iter = m_actionMap.find(method);
1851 if (iter != m_actionMap.end())
1852 return iter->second.Check(requestParameters, transport, client, notification, methodCall, outputParameters);
1854 return MethodNotFound;
1857 JSONSchemaTypeDefinitionPtr CJSONServiceDescription::GetType(const std::string &identification)
1859 std::map<std::string, JSONSchemaTypeDefinitionPtr>::iterator iter = m_types.find(identification);
1860 if (iter == m_types.end())
1861 return JSONSchemaTypeDefinitionPtr();
1863 return iter->second;
1866 bool CJSONServiceDescription::parseJSONSchemaType(const CVariant &value, std::vector<JSONSchemaTypeDefinitionPtr>& typeDefinitions, JSONSchemaType &schemaType, std::string &missingReference)
1868 missingReference.clear();
1869 schemaType = AnyValue;
1871 if (value.isArray())
1874 // If the defined type is an array, we have
1875 // to handle a union type
1876 for (unsigned int typeIndex = 0; typeIndex < value.size(); typeIndex++)
1878 JSONSchemaTypeDefinitionPtr definition = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1879 // If the type is a string try to parse it
1880 if (value[typeIndex].isString())
1881 definition->type = StringToSchemaValueType(value[typeIndex].asString());
1882 else if (value[typeIndex].isObject())
1884 if (!definition->Parse(value[typeIndex]))
1886 missingReference = definition->missingReference;
1887 CLog::Log(LOGERROR, "JSONRPC: Invalid type schema in union type definition");
1893 CLog::Log(LOGWARNING, "JSONRPC: Invalid type in union type definition");
1897 definition->optional = false;
1898 typeDefinitions.push_back(definition);
1899 parsedType |= definition->type;
1902 // If the type has not been set yet set it to "any"
1903 if (parsedType != 0)
1904 schemaType = (JSONSchemaType)parsedType;
1909 if (value.isString())
1911 schemaType = StringToSchemaValueType(value.asString());
1918 void CJSONServiceDescription::addReferenceTypeDefinition(JSONSchemaTypeDefinitionPtr typeDefinition)
1920 // If the given json value is no object or does not contain an "id" field
1921 // of type string it is no valid type definition
1922 if (typeDefinition->ID.empty())
1925 // If the id has already been defined we ignore the type definition
1926 if (m_types.find(typeDefinition->ID) != m_types.end())
1929 // Add the type to the list of type definitions
1930 m_types[typeDefinition->ID] = typeDefinition;
1932 IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(typeDefinition->ID);
1933 if (iter == m_incompleteDefinitions.end())
1936 CLog::Log(LOGINFO, "JSONRPC: Resolving incomplete types/methods referencing %s", typeDefinition->ID.c_str());
1937 for (unsigned int index = 0; index < iter->second.size(); index++)
1939 if (iter->second[index].Type == SchemaDefinitionType)
1940 AddType(iter->second[index].Schema);
1942 AddMethod(iter->second[index].Schema, iter->second[index].Method);
1945 m_incompleteDefinitions.erase(typeDefinition->ID);
1948 void CJSONServiceDescription::removeReferenceTypeDefinition(const std::string &typeID)
1953 map<string, JSONSchemaTypeDefinitionPtr>::iterator type = m_types.find(typeID);
1954 if (type != m_types.end())
1955 m_types.erase(type);
1958 void CJSONServiceDescription::getReferencedTypes(const JSONSchemaTypeDefinitionPtr type, std::vector<std::string> &referencedTypes)
1960 // If the current type is a referenceable object, we can add it to the list
1961 if (type->ID.size() > 0)
1963 for (unsigned int index = 0; index < referencedTypes.size(); index++)
1965 // The referenceable object has already been added to the list so we can just skip it
1966 if (type->ID == referencedTypes.at(index))
1970 referencedTypes.push_back(type->ID);
1973 // If the current type is an object we need to check its properties
1974 if (HasType(type->type, ObjectValue))
1976 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iter;
1977 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iterEnd = type->properties.end();
1978 for (iter = type->properties.begin(); iter != iterEnd; ++iter)
1979 getReferencedTypes(iter->second, referencedTypes);
1981 // If the current type is an array we need to check its items
1982 if (HasType(type->type, ArrayValue))
1985 for (index = 0; index < type->items.size(); index++)
1986 getReferencedTypes(type->items.at(index), referencedTypes);
1988 for (index = 0; index < type->additionalItems.size(); index++)
1989 getReferencedTypes(type->additionalItems.at(index), referencedTypes);
1992 // If the current type extends others type we need to check those types
1993 for (unsigned int index = 0; index < type->extends.size(); index++)
1994 getReferencedTypes(type->extends.at(index), referencedTypes);
1996 // If the current type is a union type we need to check those types
1997 for (unsigned int index = 0; index < type->unionTypes.size(); index++)
1998 getReferencedTypes(type->unionTypes.at(index), referencedTypes);
2001 CJSONServiceDescription::CJsonRpcMethodMap::CJsonRpcMethodMap()
2003 m_actionmap = std::map<std::string, JsonRpcMethod>();
2006 void CJSONServiceDescription::CJsonRpcMethodMap::clear()
2008 m_actionmap.clear();
2011 void CJSONServiceDescription::CJsonRpcMethodMap::add(const JsonRpcMethod &method)
2013 CStdString name = method.name;
2014 name = name.ToLower();
2015 m_actionmap[name] = method;
2018 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::begin() const
2020 return m_actionmap.begin();
2023 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::find(const std::string& key) const
2025 return m_actionmap.find(key);
2028 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::end() const
2030 return m_actionmap.end();