[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / interfaces / json-rpc / JSONServiceDescription.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
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"
27 #include "JSONRPC.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"
44
45 using namespace std;
46 using namespace JSONRPC;
47
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();
52
53 JsonRpcMethodMap CJSONServiceDescription::m_methodMaps[] = {
54 // JSON-RPC
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 },
62
63 // Player
64   { "Player.GetActivePlayers",                      CPlayerOperations::GetActivePlayers },
65   { "Player.GetProperties",                         CPlayerOperations::GetProperties },
66   { "Player.GetItem",                               CPlayerOperations::GetItem },
67
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 },
75   
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 },
81   
82   { "Player.SetAudioStream",                        CPlayerOperations::SetAudioStream },
83   { "Player.SetSubtitle",                           CPlayerOperations::SetSubtitle },
84
85 // Playlist
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 },
94
95 // Files
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 },
101
102 // Music Library
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 },
120
121 // Video Library
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 },
151   
152 // Addon operations
153   { "Addons.GetAddons",                             CAddonsOperations::GetAddons },
154   { "Addons.GetAddonDetails",                       CAddonsOperations::GetAddonDetails },
155   { "Addons.SetAddonEnabled",                       CAddonsOperations::SetAddonEnabled },
156   { "Addons.ExecuteAddon",                          CAddonsOperations::ExecuteAddon },
157
158 // GUI operations
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 },
165
166 // PVR operations
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 },
176
177 // Profiles operations
178   { "Profiles.GetProfiles",                         CProfilesOperations::GetProfiles},
179   { "Profiles.GetCurrentProfile",                   CProfilesOperations::GetCurrentProfile},
180   { "Profiles.LoadProfile",                         CProfilesOperations::LoadProfile},
181
182 // System operations
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 },
189
190 // Input operations
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 },
204
205 // Application operations
206   { "Application.GetProperties",                    CApplicationOperations::GetProperties },
207   { "Application.SetVolume",                        CApplicationOperations::SetVolume },
208   { "Application.SetMute",                          CApplicationOperations::SetMute },
209   { "Application.Quit",                             CApplicationOperations::Quit },
210
211 // Favourites operations
212   { "Favourites.GetFavourites",                     CFavouritesOperations::GetFavourites },
213   { "Favourites.AddFavourite",                      CFavouritesOperations::AddFavourite },
214
215 // Textures operations
216   { "Textures.GetTextures",                         CTextureOperations::GetTextures },
217   { "Textures.RemoveTexture",                       CTextureOperations::RemoveTexture },
218
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 },
226
227 // XBMC operations
228   { "XBMC.GetInfoLabels",                           CXBMCOperations::GetInfoLabels },
229   { "XBMC.GetInfoBooleans",                         CXBMCOperations::GetInfoBooleans }
230 };
231
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)
239 { }
240
241 bool JSONSchemaTypeDefinition::Parse(const CVariant &value, bool isParameter /* = false */)
242 {
243   bool hasReference = false;
244
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())
248   {
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)
254     {
255       CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s references an unknown type %s", name.c_str(), refType.c_str());
256       missingReference = refType;
257       return false;
258     }
259     
260     std::string typeName = name;
261     *this = *referencedTypeDef;
262     if (!typeName.empty())
263       name = typeName;
264     referencedType = referencedTypeDef;
265     hasReference = true;
266   }
267   else if (value.isMember("id") && value["id"].isString())
268     ID = GetString(value["id"], "");
269
270   // Check if the "required" field has been defined
271   optional = value.isMember("required") && value["required"].isBoolean() ? !value["required"].asBoolean() : true;
272
273   // Get the "description"
274   if (!hasReference || (value.isMember("description") && value["description"].isString()))
275     description = GetString(value["description"], "");
276
277   if (hasReference)
278   {
279     // If there is a specific default value, read it
280     if (value.isMember("default") && IsType(value["default"], type))
281     {
282       bool ok = false;
283       if (enums.size() <= 0)
284         ok = true;
285       // If the type has an enum definition we must make
286       // sure that the default value is a valid enum value
287       else
288       {
289         for (std::vector<CVariant>::const_iterator itr = enums.begin(); itr != enums.end(); ++itr)
290         {
291           if (value["default"] == *itr)
292           {
293             ok = true;
294             break;
295           }
296         }
297       }
298
299       if (ok)
300         defaultValue = value["default"];
301     }
302
303     return true;
304   }
305
306   // Check whether this type extends an existing type
307   if (value.isMember("extends"))
308   {
309     if (value["extends"].isString())
310     {
311       std::string extendsName = GetString(value["extends"], "");
312       if (!extendsName.empty())
313       {
314         JSONSchemaTypeDefinitionPtr extendedTypeDef = CJSONServiceDescription::GetType(extendsName);
315         if (extendedTypeDef.get() == NULL)
316         {
317           CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", name.c_str(), extendsName.c_str());
318           missingReference = extendsName;
319           return false;
320         }
321
322         type = extendedTypeDef->type;
323         extends.push_back(extendedTypeDef);
324       }
325     }
326     else if (value["extends"].isArray())
327     {
328       JSONSchemaType extendedType = AnyValue;
329       for (unsigned int extendsIndex = 0; extendsIndex < value["extends"].size(); extendsIndex++)
330       {
331         std::string extendsName = GetString(value["extends"][extendsIndex], "");
332         if (!extendsName.empty())
333         {
334           JSONSchemaTypeDefinitionPtr extendedTypeDef = CJSONServiceDescription::GetType(extendsName);
335           if (extendedTypeDef.get() == NULL)
336           {
337             extends.clear();
338             CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", name.c_str(), extendsName.c_str());
339             missingReference = extendsName;
340             return false;
341           }
342
343           if (extendsIndex == 0)
344             extendedType = extendedTypeDef->type;
345           else if (extendedType != extendedTypeDef->type)
346           {
347             extends.clear();
348             CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends multiple JSON schema types of mismatching types", name.c_str());
349             return false;
350           }
351
352           extends.push_back(extendedTypeDef);
353         }
354       }
355
356       type = extendedType;
357     }
358   }
359
360   // Only read the "type" attribute if it's
361   // not an extending type
362   if (extends.size() <= 0)
363   {
364     // Get the defined type of the parameter
365     if (!CJSONServiceDescription::parseJSONSchemaType(value["type"], unionTypes, type, missingReference))
366       return false;
367   }
368
369   if (HasType(type, ObjectValue))
370   {
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())
375     {
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)
379       {
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))
387         {
388           missingReference = propertyType->missingReference;
389           return false;
390         }
391         defaultValue[itr->first] = propertyType->defaultValue;
392         properties.add(propertyType);
393       }
394     }
395
396     hasAdditionalProperties = true;
397     additionalProperties = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
398     if (value.isMember("additionalProperties"))
399     {
400       if (value["additionalProperties"].isBoolean())
401       {
402         hasAdditionalProperties = value["additionalProperties"].asBoolean();
403         if (!hasAdditionalProperties)
404         {
405           additionalProperties.reset();
406         }
407       }
408       else if (value["additionalProperties"].isObject() && !value["additionalProperties"].isNull())
409       {
410         if (!additionalProperties->Parse(value["additionalProperties"]))
411         {
412           missingReference = additionalProperties->missingReference;
413           hasAdditionalProperties = false;
414           additionalProperties.reset();
415           
416           CLog::Log(LOGDEBUG, "JSONRPC: Invalid additionalProperties schema definition in type %s", name.c_str());
417           return false;
418         }
419       }
420       else
421       {
422         CLog::Log(LOGDEBUG, "JSONRPC: Invalid additionalProperties definition in type %s", name.c_str());
423         return false;
424       }
425     }
426   }
427
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))
432   {
433     // Check for "uniqueItems" field
434     if (value.isMember("uniqueItems") && value["uniqueItems"].isBoolean())
435       uniqueItems = value["uniqueItems"].asBoolean();
436     else
437       uniqueItems = false;
438
439     // Check for "additionalItems" field
440     if (value.isMember("additionalItems"))
441     {
442       // If it is an object, there is only one schema for it
443       if (value["additionalItems"].isObject())
444       {
445         JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
446         if (additionalItem->Parse(value["additionalItems"]))
447           additionalItems.push_back(additionalItem);
448         else
449         {
450           CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" value for type %s", name.c_str());
451           missingReference = additionalItem->missingReference;
452           return false;
453         }
454       }
455       // If it is an array there may be multiple schema definitions
456       else if (value["additionalItems"].isArray())
457       {
458         for (unsigned int itemIndex = 0; itemIndex < value["additionalItems"].size(); itemIndex++)
459         {
460           JSONSchemaTypeDefinitionPtr additionalItem = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
461
462           if (additionalItem->Parse(value["additionalItems"][itemIndex]))
463             additionalItems.push_back(additionalItem);
464           else
465           {
466             CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" value (item %d) for type %s", itemIndex, name.c_str());
467             missingReference = additionalItem->missingReference;
468             return false;
469           }
470         }
471       }
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())
475       {
476         CLog::Log(LOGDEBUG, "Invalid \"additionalItems\" definition for type %s", name.c_str());
477         return false;
478       }
479     }
480
481     // If the "items" field is a single object
482     // we can parse that directly
483     if (value.isMember("items"))
484     {
485       if (value["items"].isObject())
486       {
487         JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
488         if (!item->Parse(value["items"]))
489         {
490           CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" for type %s", name.c_str());
491           missingReference = item->missingReference;
492           return false;
493         }
494         items.push_back(item);
495       }
496       // Otherwise if it is an array we need to
497       // parse all elements and store them
498       else if (value["items"].isArray())
499       {
500         for (CVariant::const_iterator_array itemItr = value["items"].begin_array(); itemItr != value["items"].end_array(); ++itemItr)
501         {
502           JSONSchemaTypeDefinitionPtr item = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
503           if (!item->Parse(*itemItr))
504           {
505             CLog::Log(LOGDEBUG, "Invalid item definition in \"items\" array for type %s", name.c_str());
506             missingReference = item->missingReference;
507             return false;
508           }
509           items.push_back(item);
510         }
511       }
512     }
513
514     minItems = (unsigned int)value["minItems"].asUnsignedInteger(0);
515     maxItems = (unsigned int)value["maxItems"].asUnsignedInteger(0);
516   }
517
518   if (HasType(type, NumberValue) || HasType(type, IntegerValue))
519   {
520     if ((type & NumberValue) == NumberValue)
521     {
522       minimum = value["minimum"].asDouble(-numeric_limits<double>::max());
523       maximum = value["maximum"].asDouble(numeric_limits<double>::max());
524     }
525     else if ((type  & IntegerValue) == IntegerValue)
526     {
527       minimum = (double)value["minimum"].asInteger(numeric_limits<int>::min());
528       maximum = (double)value["maximum"].asInteger(numeric_limits<int>::max());
529     }
530
531     exclusiveMinimum = value["exclusiveMinimum"].asBoolean(false);
532     exclusiveMaximum = value["exclusiveMaximum"].asBoolean(false);
533     divisibleBy = (unsigned int)value["divisibleBy"].asUnsignedInteger(0);
534   }
535       
536   if (HasType(type, StringValue))
537   {
538     minLength = (int)value["minLength"].asInteger(-1);
539     maxLength = (int)value["maxLength"].asInteger(-1);
540   }
541
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())
546   {
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)
549     {
550       // Check for duplicates and eliminate them
551       bool approved = true;
552       for (unsigned int approvedIndex = 0; approvedIndex < enums.size(); approvedIndex++)
553       {
554         if (*enumItr == enums.at(approvedIndex))
555         {
556           approved = false;
557           break;
558         }
559       }
560
561       // Only add the current item to the enum value 
562       // list if it is not duplicate
563       if (approved)
564         enums.push_back(*enumItr);
565     }
566   }
567
568   if (type != ObjectValue)
569   {
570     // If there is a definition for a default value and its type
571     // matches the type of the parameter we can parse it
572     bool ok = false;
573     if (value.isMember("default") && IsType(value["default"], type))
574     {
575       if (enums.size() <= 0)
576         ok = true;
577       // If the type has an enum definition we must make
578       // sure that the default value is a valid enum value
579       else
580       {
581         for (std::vector<CVariant>::const_iterator itr = enums.begin(); itr != enums.end(); ++itr)
582         {
583           if (value["default"] == *itr)
584           {
585             ok = true;
586             break;
587           }
588         }
589       }
590     }
591
592     if (ok)
593       defaultValue = value["default"];
594     else
595     {
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());
600       
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
606       else
607         SetDefaultValue(defaultValue, type);
608     }
609   }
610
611   return true;
612 }
613
614 JSONRPC_STATUS JSONSchemaTypeDefinition::Check(const CVariant &value, CVariant &outputValue, CVariant &errorData)
615 {
616   if (!name.empty())
617     errorData["name"] = name;
618   SchemaValueTypeToJson(type, errorData["type"]);
619   CStdString errorMessage;
620
621   if (referencedType != NULL && !referencedTypeSet)
622     Set(referencedType);
623
624   // Let's check the type of the provided parameter
625   if (!IsType(value, type))
626   {
627     errorMessage = StringUtils::Format("Invalid type %s received", ValueTypeToString(value.type()));
628     errorData["message"] = errorMessage.c_str();
629     return InvalidParams;
630   }
631   else if (value.isNull() && !HasType(type, NullValue))
632   {
633     errorData["message"] = "Received value is null";
634     return InvalidParams;
635   }
636
637   // Let's check if we have to handle a union type
638   if (unionTypes.size() > 0)
639   {
640     bool ok = false;
641     for (unsigned int unionIndex = 0; unionIndex < unionTypes.size(); unionIndex++)
642     {
643       CVariant dummyError;
644       CVariant testOutput = outputValue;
645       if (unionTypes.at(unionIndex)->Check(value, testOutput, dummyError) == OK)
646       {
647         ok = true;
648         outputValue = testOutput;
649         break;
650       }
651     }
652
653     if (!ok)
654     {
655       errorData["message"] = "Received value does not match any of the union type definitions";
656       return InvalidParams;
657     }
658   }
659
660   // First we need to check if this type extends another
661   // type and if so we need to check against the extended
662   // type first
663   if (extends.size() > 0)
664   {
665     for (unsigned int extendsIndex = 0; extendsIndex < extends.size(); extendsIndex++)
666     {
667       JSONRPC_STATUS status = extends.at(extendsIndex)->Check(value, outputValue, errorData);
668
669       if (status != OK)
670       {
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();
674         return status;
675       }
676     }
677   }
678
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())
683   {
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))
687     {
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());
693       else
694         errorMessage = StringUtils::Format("Only %d array items expected but %d received", maxItems, value.size());
695       errorData["message"] = errorMessage.c_str();
696       return InvalidParams;
697     }
698
699     if (items.size() == 0)
700       outputValue = value;
701     else if (items.size() == 1)
702     {
703       JSONSchemaTypeDefinitionPtr itemType = items.at(0);
704
705       // Loop through all array elements
706       for (unsigned int arrayIndex = 0; arrayIndex < value.size(); arrayIndex++)
707       {
708         CVariant temp;
709         JSONRPC_STATUS status = itemType->Check(value[arrayIndex], temp, errorData["property"]);
710         outputValue.push_back(temp);
711         if (status != OK)
712         {
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();
716           return status;
717         }
718       }
719     }
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
724     // "items" array
725     else
726     {
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))
732       {
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;
737       }
738
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++)
744       {
745         JSONRPC_STATUS status = items.at(arrayIndex)->Check(value[arrayIndex], outputValue[arrayIndex], errorData["property"]);
746         if (status != OK)
747         {
748           CLog::Log(LOGDEBUG, "JSONRPC: Array element at index %u does not match with items schema in type %s", arrayIndex, name.c_str());
749           return status;
750         }
751       }
752
753       if (additionalItems.size() > 0)
754       {
755         // Loop through the rest of the elements
756         // in the array and check them against the
757         // "additionalItems"
758         for (; arrayIndex < value.size(); arrayIndex++)
759         {
760           bool ok = false;
761           for (unsigned int additionalIndex = 0; additionalIndex < additionalItems.size(); additionalIndex++)
762           {
763             CVariant dummyError;
764             if (additionalItems.at(additionalIndex)->Check(value[arrayIndex], outputValue[arrayIndex], dummyError) == OK)
765             {
766               ok = true;
767               break;
768             }
769           }
770
771           if (!ok)
772           {
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;
777           }
778         }
779       }
780     }
781
782     // If every array element is unique we need to check each one
783     if (uniqueItems)
784     {
785       for (unsigned int checkingIndex = 0; checkingIndex < outputValue.size(); checkingIndex++)
786       {
787         for (unsigned int checkedIndex = checkingIndex + 1; checkedIndex < outputValue.size(); checkedIndex++)
788         {
789           // If two elements are the same they are not unique
790           if (outputValue[checkingIndex] == outputValue[checkedIndex])
791           {
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;
796           }
797         }
798       }
799     }
800
801     return OK;
802   }
803
804   // If it is an object we need to check every element
805   // against the defined "properties"
806   if (HasType(type, ObjectValue) && value.isObject())
807   {
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)
812     {
813       if (value.isMember(propertiesIterator->second->name))
814       {
815         JSONRPC_STATUS status = propertiesIterator->second->Check(value[propertiesIterator->second->name], outputValue[propertiesIterator->second->name], errorData["property"]);
816         if (status != OK)
817         {
818           CLog::Log(LOGDEBUG, "JSONRPC: Invalid property \"%s\" in type %s", propertiesIterator->second->name.c_str(), name.c_str());
819           return status;
820         }
821         handled++;
822       }
823       else if (propertiesIterator->second->optional)
824         outputValue[propertiesIterator->second->name] = propertiesIterator->second->defaultValue;
825       else
826       {
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;
831       }
832     }
833
834     // Additional properties are not allowed
835     if (handled < value.size())
836     {
837       // If additional properties are allowed we need to check if
838       // they match the defined schema
839       if (hasAdditionalProperties && additionalProperties != NULL)
840       {
841         CVariant::const_iterator_map iter;
842         CVariant::const_iterator_map iterEnd = value.end_map();
843         for (iter = value.begin_map(); iter != iterEnd; iter++)
844         {
845           if (properties.find(iter->first) != properties.end())
846             continue;
847
848           // If the additional property is of type "any"
849           // we can simply copy its value to the output
850           // object
851           if (additionalProperties->type == AnyValue)
852           {
853             outputValue[iter->first] = value[iter->first];
854             continue;
855           }
856
857           JSONRPC_STATUS status = additionalProperties->Check(value[iter->first], outputValue[iter->first], errorData["property"]);
858           if (status != OK)
859           {
860             CLog::Log(LOGDEBUG, "JSONRPC: Invalid additional property \"%s\" in type %s", iter->first.c_str(), name.c_str());
861             return status;
862           }
863         }
864       }
865       // If we still have unchecked properties but additional
866       // properties are not allowed, we have invalid parameters
867       else if (!hasAdditionalProperties || additionalProperties == NULL)
868       {
869         errorData["message"] = "Unexpected additional properties received";
870         errorData.erase("property");
871         return InvalidParams;
872       }
873     }
874
875     return OK;
876   }
877
878   // It's neither an array nor an object
879
880   // If it can only take certain values ("enum")
881   // we need to check against those
882   if (enums.size() > 0)
883   {
884     bool valid = false;
885     for (std::vector<CVariant>::const_iterator enumItr = enums.begin(); enumItr != enums.end(); ++enumItr)
886     {
887       if (*enumItr == value)
888       {
889         valid = true;
890         break;
891       }
892     }
893
894     if (!valid)
895     {
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;
899     }
900   }
901
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()))
905   {
906     double numberValue;
907     if (value.isDouble())
908       numberValue = value.asDouble();
909     else
910       numberValue = (double)value.asInteger();
911     // Check minimum
912     if ((exclusiveMinimum && numberValue <= minimum) || (!exclusiveMinimum && numberValue < minimum) ||
913     // Check maximum
914         (exclusiveMaximum && numberValue >= maximum) || (!exclusiveMaximum && numberValue > maximum))        
915     {
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);
920       else
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;
925     }
926     // Check divisibleBy
927     if ((HasType(type, IntegerValue) && divisibleBy > 0 && ((int)numberValue % divisibleBy) != 0))
928     {
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;
933     }
934   }
935
936   // If we have a string, we need to check the length
937   if (HasType(type, StringValue) && value.isString())
938   {
939     int size = value.asString().size();
940     if (size < minLength)
941     {
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;
946     }
947
948     if (maxLength >= 0 && size > maxLength)
949     {
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;
954     }
955   }
956
957   // Otherwise it can have any value
958   outputValue = value;
959   return OK;
960 }
961
962 void JSONSchemaTypeDefinition::Print(bool isParameter, bool isGlobal, bool printDefault, bool printDescriptions, CVariant &output) const
963 {
964   bool typeReference = false;
965
966   // Printing general fields
967   if (isParameter)
968     output["name"] = name;
969
970   if (isGlobal)
971     output["id"] = ID;
972   else if (!ID.empty())
973   {
974     output["$ref"] = ID;
975     typeReference = true;
976   }
977
978   if (printDescriptions && !description.empty())
979     output["description"] = description;
980
981   if (isParameter || printDefault)
982   {
983     if (!optional)
984       output["required"] = true;
985     if (optional && type != ObjectValue && type != ArrayValue)
986       output["default"] = defaultValue;
987   }
988
989   if (!typeReference)
990   {
991     if (extends.size() == 1)
992     {
993       output["extends"] = extends.at(0)->ID;
994     }
995     else if (extends.size() > 1)
996     {
997       output["extends"] = CVariant(CVariant::VariantTypeArray);
998       for (unsigned int extendsIndex = 0; extendsIndex < extends.size(); extendsIndex++)
999         output["extends"].append(extends.at(extendsIndex)->ID);
1000     }
1001     else if (unionTypes.size() > 0)
1002     {
1003       output["type"] = CVariant(CVariant::VariantTypeArray);
1004       for (unsigned int unionIndex = 0; unionIndex < unionTypes.size(); unionIndex++)
1005       {
1006         CVariant unionOutput = CVariant(CVariant::VariantTypeObject);
1007         unionTypes.at(unionIndex)->Print(false, false, false, printDescriptions, unionOutput);
1008         output["type"].append(unionOutput);
1009       }
1010     }
1011     else
1012       CJSONUtils::SchemaValueTypeToJson(type, output["type"]);
1013
1014     // Printing enum field
1015     if (enums.size() > 0)
1016     {
1017       output["enums"] = CVariant(CVariant::VariantTypeArray);
1018       for (unsigned int enumIndex = 0; enumIndex < enums.size(); enumIndex++)
1019         output["enums"].append(enums.at(enumIndex));
1020     }
1021
1022     // Printing integer/number fields
1023     if (CJSONUtils::HasType(type, IntegerValue) || CJSONUtils::HasType(type, NumberValue))
1024     {
1025       if (CJSONUtils::HasType(type, NumberValue))
1026       {
1027         if (minimum > -numeric_limits<double>::max())
1028           output["minimum"] = minimum;
1029         if (maximum < numeric_limits<double>::max())
1030           output["maximum"] = maximum;
1031       }
1032       else
1033       {
1034         if (minimum > numeric_limits<int>::min())
1035           output["minimum"] = (int)minimum;
1036         if (maximum < numeric_limits<int>::max())
1037           output["maximum"] = (int)maximum;
1038       }
1039
1040       if (exclusiveMinimum)
1041         output["exclusiveMinimum"] = true;
1042       if (exclusiveMaximum)
1043         output["exclusiveMaximum"] = true;
1044       if (divisibleBy > 0)
1045         output["divisibleBy"] = divisibleBy;
1046     }
1047     if (CJSONUtils::HasType(type, StringValue))
1048     {
1049       if (minLength >= 0)
1050         output["minLength"] = minLength;
1051       if (maxLength >= 0)
1052         output["maxLength"] = maxLength;
1053     }
1054
1055     // Print array fields
1056     if (CJSONUtils::HasType(type, ArrayValue))
1057     {
1058       if (items.size() == 1)
1059       {
1060         items.at(0)->Print(false, false, false, printDescriptions, output["items"]);
1061       }
1062       else if (items.size() > 1)
1063       {
1064         output["items"] = CVariant(CVariant::VariantTypeArray);
1065         for (unsigned int itemIndex = 0; itemIndex < items.size(); itemIndex++)
1066         {
1067           CVariant item = CVariant(CVariant::VariantTypeObject);
1068           items.at(itemIndex)->Print(false, false, false, printDescriptions, item);
1069           output["items"].append(item);
1070         }
1071       }
1072
1073       if (minItems > 0)
1074         output["minItems"] = minItems;
1075       if (maxItems > 0)
1076         output["maxItems"] = maxItems;
1077
1078       if (additionalItems.size() == 1)
1079       {
1080         additionalItems.at(0)->Print(false, false, false, printDescriptions, output["additionalItems"]);
1081       }
1082       else if (additionalItems.size() > 1)
1083       {
1084         output["additionalItems"] = CVariant(CVariant::VariantTypeArray);
1085         for (unsigned int addItemIndex = 0; addItemIndex < additionalItems.size(); addItemIndex++)
1086         {
1087           CVariant item = CVariant(CVariant::VariantTypeObject);
1088           additionalItems.at(addItemIndex)->Print(false, false, false, printDescriptions, item);
1089           output["additionalItems"].append(item);
1090         }
1091       }
1092
1093       if (uniqueItems)
1094         output["uniqueItems"] = true;
1095     }
1096
1097     // Print object fields
1098     if (CJSONUtils::HasType(type, ObjectValue))
1099     {
1100       if (properties.size() > 0)
1101       {
1102         output["properties"] = CVariant(CVariant::VariantTypeObject);
1103
1104         JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesEnd = properties.end();
1105         JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesIterator;
1106         for (propertiesIterator = properties.begin(); propertiesIterator != propertiesEnd; ++propertiesIterator)
1107         {
1108           propertiesIterator->second->Print(false, false, true, printDescriptions, output["properties"][propertiesIterator->first]);
1109         }
1110       }
1111
1112       if (!hasAdditionalProperties)
1113         output["additionalProperties"] = false;
1114       else if (additionalProperties != NULL && additionalProperties->type != AnyValue)
1115         additionalProperties->Print(false, false, true, printDescriptions, output["additionalProperties"]);
1116     }
1117   }
1118 }
1119
1120 void JSONSchemaTypeDefinition::Set(const JSONSchemaTypeDefinitionPtr typeDefinition)
1121 {
1122   if (typeDefinition.get() == NULL)
1123     return;
1124
1125   string origName = name;
1126   string origDescription = description;
1127   bool origOptional = optional;
1128   CVariant origDefaultValue = defaultValue;
1129   JSONSchemaTypeDefinitionPtr referencedTypeDef = referencedType;
1130
1131   // set all the values from the given type definition
1132   *this = *typeDefinition;
1133
1134   // restore the original values
1135   if (!origName.empty())
1136     name = origName;
1137
1138   if (!origDescription.empty())
1139     description = origDescription;
1140
1141   if (!origOptional)
1142     optional = origOptional;
1143
1144   if (!origDefaultValue.isNull())
1145     defaultValue = origDefaultValue;
1146
1147   if (referencedTypeDef.get() != NULL)
1148     referencedType = referencedTypeDef;
1149
1150   referencedTypeSet = true;
1151 }
1152
1153 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::CJsonSchemaPropertiesMap()
1154 {
1155   m_propertiesmap = std::map<std::string, JSONSchemaTypeDefinitionPtr>();
1156 }
1157
1158 void JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::add(JSONSchemaTypeDefinitionPtr property)
1159 {
1160   CStdString name = property->name;
1161   name = name.ToLower();
1162   m_propertiesmap[name] = property;
1163 }
1164
1165 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::begin() const
1166 {
1167   return m_propertiesmap.begin();
1168 }
1169
1170 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::find(const std::string& key) const
1171 {
1172   return m_propertiesmap.find(key);
1173 }
1174
1175 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::end() const
1176 {
1177   return m_propertiesmap.end();
1178 }
1179
1180 unsigned int JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::size() const
1181 {
1182   return m_propertiesmap.size();
1183 }
1184
1185 JsonRpcMethod::JsonRpcMethod()
1186   : missingReference(""), method(NULL),
1187     returns(new JSONSchemaTypeDefinition())
1188 { }
1189
1190 bool JsonRpcMethod::Parse(const CVariant &value)
1191 {
1192   // Parse XBMC specific information about the method
1193   if (value.isMember("transport") && value["transport"].isArray())
1194   {
1195     int transport = 0;
1196     for (unsigned int index = 0; index < value["transport"].size(); index++)
1197       transport |= StringToTransportLayer(value["transport"][index].asString());
1198
1199     transportneed = (TransportLayerCapability)transport;
1200   }
1201   else
1202     transportneed = StringToTransportLayer(value.isMember("transport") ? value["transport"].asString() : "");
1203
1204   if (value.isMember("permission") && value["permission"].isArray())
1205   {
1206     int permissions = 0;
1207     for (unsigned int index = 0; index < value["permission"].size(); index++)
1208       permissions |= StringToPermission(value["permission"][index].asString());
1209
1210     permission = (OperationPermission)permissions;
1211   }
1212   else
1213     permission = StringToPermission(value.isMember("permission") ? value["permission"].asString() : "");
1214
1215   description = GetString(value["description"], "");
1216
1217   // Check whether there are parameters defined
1218   if (value.isMember("params") && value["params"].isArray())
1219   {
1220     // Loop through all defined parameters
1221     for (unsigned int paramIndex = 0; paramIndex < value["params"].size(); paramIndex++)
1222     {
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()))
1231       {
1232         CLog::Log(LOGDEBUG, "JSONRPC: Method %s has a badly defined parameter", name.c_str());
1233         return false;
1234       }
1235
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))
1240       {
1241         missingReference = param->missingReference;
1242         return false;
1243       }
1244       parameters.push_back(param);
1245     }
1246   }
1247     
1248   // Parse the return value of the method
1249   if (!parseReturn(value))
1250   {
1251     missingReference = returns->missingReference;
1252     return false;
1253   }
1254
1255   return true;
1256 }
1257
1258 JSONRPC_STATUS JsonRpcMethod::Check(const CVariant &requestParameters, ITransportLayer *transport, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters) const
1259 {
1260   if (transport != NULL && (transport->GetCapabilities() & transportneed) == transportneed)
1261   {
1262     if (client != NULL && (client->GetPermissionFlags() & permission) == permission && (!notification || (permission & OPERATION_PERMISSION_NOTIFICATION) == permission))
1263     {
1264       methodCall = method;
1265
1266       // Count the number of actually handled (present)
1267       // parameters
1268       unsigned int handled = 0;
1269       CVariant errorData = CVariant(CVariant::VariantTypeObject);
1270       errorData["method"] = name;
1271
1272       // Loop through all the parameters to check
1273       for (unsigned int i = 0; i < parameters.size(); i++)
1274       {
1275         // Evaluate the current parameter
1276         JSONRPC_STATUS status = checkParameter(requestParameters, parameters.at(i), i, outputParameters, handled, errorData);
1277         if (status != OK)
1278         {
1279           // Return the error data object in the outputParameters reference
1280           outputParameters = errorData;
1281           return status;
1282         }
1283       }
1284
1285       // Check if there were unnecessary parameters
1286       if (handled < requestParameters.size())
1287       {
1288         errorData["message"] = "Too many parameters";
1289         outputParameters = errorData;
1290         return InvalidParams;
1291       }
1292
1293       return OK;
1294     }
1295     else
1296       return BadPermission;
1297   }
1298   
1299   return MethodNotFound;
1300 }
1301
1302 bool JsonRpcMethod::parseParameter(const CVariant &value, JSONSchemaTypeDefinitionPtr parameter)
1303 {
1304   parameter->name = GetString(value["name"], "");
1305
1306   // Parse the type and default value of the parameter
1307   return parameter->Parse(value, true);
1308 }
1309
1310 bool JsonRpcMethod::parseReturn(const CVariant &value)
1311 {
1312   // Only parse the "returns" definition if there is one
1313   if (!value.isMember("returns"))
1314   {
1315     returns->type = NullValue;
1316     return true;
1317   }
1318   
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);
1322   
1323   // otherwise we have to parse the whole type definition
1324   if (!returns->Parse(value["returns"]))
1325   {
1326     missingReference = returns->missingReference;
1327     return false;
1328   }
1329   
1330   return true;
1331 }
1332
1333 JSONRPC_STATUS JsonRpcMethod::checkParameter(const CVariant &requestParameters, JSONSchemaTypeDefinitionPtr type, unsigned int position, CVariant &outputParameters, unsigned int &handled, CVariant &errorData)
1334 {
1335   // Let's check if the parameter has been provided
1336   if (ParameterExists(requestParameters, type->name, position))
1337   {
1338     // Get the parameter
1339     CVariant parameterValue = GetParameter(requestParameters, type->name, position);
1340
1341     // Evaluate the type of the parameter
1342     JSONRPC_STATUS status = type->Check(parameterValue, outputParameters[type->name], errorData["stack"]);
1343     if (status != OK)
1344       return status;
1345
1346     // The parameter was present and valid
1347     handled++;
1348   }
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
1354   else
1355   {
1356     errorData["stack"]["name"] = type->name;
1357     SchemaValueTypeToJson(type->type, errorData["stack"]["type"]);
1358     errorData["stack"]["message"] = "Missing parameter";
1359     return InvalidParams;
1360   }
1361
1362   return OK;
1363 }
1364
1365 void CJSONServiceDescription::Cleanup()
1366 {
1367   // reset all of the static data
1368   m_notifications.clear();
1369   m_actionMap.clear();
1370   m_types.clear();
1371   m_incompleteDefinitions.clear();
1372 }
1373
1374 bool CJSONServiceDescription::prepareDescription(std::string &description, CVariant &descriptionObject, std::string &name)
1375 {
1376   if (description.empty())
1377   {
1378     CLog::Log(LOGERROR, "JSONRPC: Missing JSON Schema definition for \"%s\"", name.c_str());
1379     return false;
1380   }
1381
1382   if (description.at(0) != '{')
1383   {
1384     description = StringUtils::Format("{%s}", description.c_str());
1385   }
1386
1387   descriptionObject = CJSONVariantParser::Parse((const unsigned char *)description.c_str(), description.size());
1388
1389   // Make sure the method description actually exists and represents an object
1390   if (!descriptionObject.isObject())
1391   {
1392     CLog::Log(LOGERROR, "JSONRPC: Unable to parse JSON Schema definition for \"%s\"", name.c_str());
1393     return false;
1394   }
1395
1396   CVariant::const_iterator_map member = descriptionObject.begin_map();
1397   if (member != descriptionObject.end_map())
1398     name = member->first;
1399
1400   if (name.empty() ||
1401      (!descriptionObject[name].isMember("type") && !descriptionObject[name].isMember("$ref") && !descriptionObject[name].isMember("extends")))
1402   {
1403     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for \"%s\"", name.c_str());
1404     return false;
1405   }
1406
1407   return true;
1408 }
1409
1410 bool CJSONServiceDescription::addMethod(const std::string &jsonMethod, MethodCall method)
1411 {
1412   CVariant descriptionObject;
1413   std::string methodName;
1414
1415   std::string modJsonMethod = jsonMethod;
1416   // Make sure the method description actually exists and represents an object
1417   if (!prepareDescription(modJsonMethod, descriptionObject, methodName))
1418   {
1419     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for method \"%s\"", methodName.c_str());
1420     return false;
1421   }
1422
1423   if (m_actionMap.find(methodName) != m_actionMap.end())
1424   {
1425     CLog::Log(LOGERROR, "JSONRPC: There already is a method with the name \"%s\"", methodName.c_str());
1426     return false;
1427   }
1428
1429   std::string type = GetString(descriptionObject[methodName]["type"], "");
1430   if (type.compare("method") != 0)
1431   {
1432     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for method \"%s\"", methodName.c_str());
1433     return false;
1434   }
1435
1436   if (method == NULL)
1437   {
1438     unsigned int size = sizeof(m_methodMaps) / sizeof(JsonRpcMethodMap);
1439     for (unsigned int index = 0; index < size; index++)
1440     {
1441       if (methodName.compare(m_methodMaps[index].name) == 0)
1442       {
1443         method = m_methodMaps[index].method;
1444         break;
1445       }
1446     }
1447
1448     if (method == NULL)
1449     {
1450       CLog::Log(LOGERROR, "JSONRPC: Missing implementation for method \"%s\"", methodName.c_str());
1451       return false;
1452     }
1453   }
1454
1455   // Parse the details of the method
1456   JsonRpcMethod newMethod;
1457   newMethod.name = methodName;
1458   newMethod.method = method;
1459   
1460   if (!newMethod.Parse(descriptionObject[newMethod.name]))
1461   {
1462     CLog::Log(LOGERROR, "JSONRPC: Could not parse method \"%s\"", methodName.c_str());
1463     if (!newMethod.missingReference.empty())
1464     {
1465       IncompleteSchemaDefinition incomplete;
1466       incomplete.Schema = modJsonMethod;
1467       incomplete.Type = SchemaDefinitionMethod;
1468       incomplete.Method = method;
1469       
1470       IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(newMethod.missingReference);
1471       if (iter == m_incompleteDefinitions.end())
1472         m_incompleteDefinitions[newMethod.missingReference] = std::vector<IncompleteSchemaDefinition>();
1473       
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);
1476     }
1477     
1478     return false;
1479   }
1480
1481   m_actionMap.add(newMethod);
1482
1483   return true;
1484 }
1485
1486 bool CJSONServiceDescription::AddType(const std::string &jsonType)
1487 {
1488   CVariant descriptionObject;
1489   std::string typeName;
1490
1491   std::string modJsonType = jsonType;
1492   if (!prepareDescription(modJsonType, descriptionObject, typeName))
1493   {
1494     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for type \"%s\"", typeName.c_str());
1495     return false;
1496   }
1497
1498   if (m_types.find(typeName) != m_types.end())
1499   {
1500     CLog::Log(LOGERROR, "JSONRPC: There already is a type with the name \"%s\"", typeName.c_str());
1501     return false;
1502   }
1503
1504   // Make sure the "id" attribute is correctly populated
1505   descriptionObject[typeName]["id"] = typeName;
1506
1507   JSONSchemaTypeDefinitionPtr globalType = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1508   globalType->name = typeName;
1509   globalType->ID = typeName;
1510   CJSONServiceDescription::addReferenceTypeDefinition(globalType);
1511
1512   if (!globalType->Parse(descriptionObject[typeName]))
1513   {
1514     CLog::Log(LOGERROR, "JSONRPC: Could not parse type \"%s\"", typeName.c_str());
1515     CJSONServiceDescription::removeReferenceTypeDefinition(typeName);
1516     if (!globalType->missingReference.empty())
1517     {
1518       IncompleteSchemaDefinition incomplete;
1519       incomplete.Schema = modJsonType;
1520       incomplete.Type = SchemaDefinitionType;
1521       
1522       IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(globalType->missingReference);
1523       if (iter == m_incompleteDefinitions.end())
1524         m_incompleteDefinitions[globalType->missingReference] = std::vector<IncompleteSchemaDefinition>();
1525       
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);
1528     }
1529
1530     globalType.reset();
1531     
1532     return false;
1533   }
1534
1535   return true;
1536 }
1537
1538 bool CJSONServiceDescription::AddMethod(const std::string &jsonMethod, MethodCall method)
1539 {
1540   if (method == NULL)
1541   {
1542     CLog::Log(LOGERROR, "JSONRPC: Invalid JSONRPC method implementation");
1543     return false;
1544   }
1545
1546   return addMethod(jsonMethod, method);
1547 }
1548
1549 bool CJSONServiceDescription::AddBuiltinMethod(const std::string &jsonMethod)
1550 {
1551   return addMethod(jsonMethod, NULL);
1552 }
1553
1554 bool CJSONServiceDescription::AddNotification(const std::string &jsonNotification)
1555 {
1556   CVariant descriptionObject;
1557   std::string notificationName;
1558
1559   std::string modJsonNotification = jsonNotification;
1560   // Make sure the notification description actually exists and represents an object
1561   if (!prepareDescription(modJsonNotification, descriptionObject, notificationName))
1562   {
1563     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for notification \"%s\"", notificationName.c_str());
1564     return false;
1565   }
1566
1567   if (m_notifications.find(notificationName) != m_notifications.end())
1568   {
1569     CLog::Log(LOGERROR, "JSONRPC: There already is a notification with the name \"%s\"", notificationName.c_str());
1570     return false;
1571   }
1572
1573   std::string type = GetString(descriptionObject[notificationName]["type"], "");
1574   if (type.compare("notification") != 0)
1575   {
1576     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for notification \"%s\"", notificationName.c_str());
1577     return false;
1578   }
1579
1580   m_notifications[notificationName] = descriptionObject;
1581
1582   return true;
1583 }
1584
1585 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<CVariant> &values, CVariant::VariantType type /* = CVariant::VariantTypeNull */, const CVariant &defaultValue /* = CVariant::ConstNullVariant */)
1586 {
1587   if (name.empty() || m_types.find(name) != m_types.end() ||
1588       values.size() == 0)
1589     return false;
1590
1591   JSONSchemaTypeDefinitionPtr definition = JSONSchemaTypeDefinitionPtr(new JSONSchemaTypeDefinition());
1592   definition->ID = name;
1593
1594   std::vector<CVariant::VariantType> types;
1595   bool autoType = false;
1596   if (type == CVariant::VariantTypeNull)
1597     autoType = true;
1598   else
1599     types.push_back(type);
1600
1601   for (unsigned int index = 0; index < values.size(); index++)
1602   {
1603     if (autoType)
1604       types.push_back(values[index].type());
1605     else if (type != CVariant::VariantTypeConstNull && type != values[index].type())
1606       return false;
1607   }
1608   definition->enums.insert(definition->enums.begin(), values.begin(), values.end());
1609
1610   int schemaType = (int)AnyValue;
1611   for (unsigned int index = 0; index < types.size(); index++)
1612   {
1613     JSONSchemaType currentType;
1614     switch (type)
1615     {
1616       case CVariant::VariantTypeString:
1617         currentType = StringValue;
1618         break;
1619       case CVariant::VariantTypeDouble:
1620         currentType = NumberValue;
1621         break;
1622       case CVariant::VariantTypeInteger:
1623       case CVariant::VariantTypeUnsignedInteger:
1624         currentType = IntegerValue;
1625         break;
1626       case CVariant::VariantTypeBoolean:
1627         currentType = BooleanValue;
1628         break;
1629       case CVariant::VariantTypeArray:
1630         currentType = ArrayValue;
1631         break;
1632       case CVariant::VariantTypeObject:
1633         currentType = ObjectValue;
1634         break;
1635       case CVariant::VariantTypeConstNull:
1636         currentType = AnyValue;
1637         break;
1638       default:
1639       case CVariant::VariantTypeNull:
1640         return false;
1641     }
1642
1643     if (index == 0)
1644       schemaType = currentType;
1645     else
1646       schemaType |= (int)currentType;
1647   }
1648   definition->type = (JSONSchemaType)schemaType;
1649   
1650   if (defaultValue.type() == CVariant::VariantTypeConstNull)
1651     definition->defaultValue = definition->enums.at(0);
1652   else
1653     definition->defaultValue = defaultValue;
1654
1655   addReferenceTypeDefinition(definition);
1656
1657   return true;
1658 }
1659
1660 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<std::string> &values)
1661 {
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));
1665
1666   return AddEnum(name, enums, CVariant::VariantTypeString);
1667 }
1668
1669 bool CJSONServiceDescription::AddEnum(const std::string &name, const std::vector<int> &values)
1670 {
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));
1674
1675   return AddEnum(name, enums, CVariant::VariantTypeInteger);
1676 }
1677
1678 const char* CJSONServiceDescription::GetVersion()
1679 {
1680   return JSONRPC_SERVICE_VERSION;
1681 }
1682
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 */)
1686 {
1687   std::map<std::string, JSONSchemaTypeDefinitionPtr> types;
1688   CJsonRpcMethodMap methods;
1689   std::map<std::string, CVariant> notifications;
1690
1691   int clientPermissions = client->GetPermissionFlags();
1692   int transportCapabilities = transport->GetCapabilities();
1693
1694   if (filterByName.size() > 0)
1695   {
1696     CStdString name = filterByName;
1697
1698     if (filterByType == "method")
1699     {
1700       name = name.ToLower();
1701
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);
1706       else
1707         return InvalidParams;
1708     }
1709     else if (filterByType == "namespace")
1710     {
1711       // append a . delimiter to make sure we check for a namespace
1712       name = name.ToLower().append(".");
1713
1714       CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
1715       CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = m_actionMap.end();
1716       for (methodIterator = m_actionMap.begin(); methodIterator != methodIteratorEnd; methodIterator++)
1717       {
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);
1722       }
1723
1724       if (methods.begin() == methods.end())
1725         return InvalidParams;
1726     }
1727     else if (filterByType == "type")
1728     {
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;
1732       else
1733         return InvalidParams;
1734     }
1735     else if (filterByType == "notification")
1736     {
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;
1740       else
1741         return InvalidParams;
1742     }
1743     else
1744       return InvalidParams;
1745
1746     // If we need to print all referenced types we have to go through all parameters etc
1747     if (printReferences)
1748     {
1749       std::vector<std::string> referencedTypes;
1750
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);
1756
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++)
1761       {
1762         for (unsigned int index = 0; index < methodIterator->second.parameters.size(); index++)
1763           getReferencedTypes(methodIterator->second.parameters.at(index), referencedTypes);
1764
1765         getReferencedTypes(methodIterator->second.returns, referencedTypes);
1766       }
1767
1768       for (unsigned int index = 0; index < referencedTypes.size(); index++)
1769       {
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;
1773       }
1774     }
1775   }
1776   else
1777   {
1778     types = m_types;
1779     methods = m_actionMap;
1780     notifications = m_notifications;
1781   }
1782
1783   // Print the header
1784   result["id"] = JSONRPC_SERVICE_ID;
1785   result["version"] = JSONRPC_SERVICE_VERSION;
1786   result["description"] = JSONRPC_SERVICE_DESCRIPTION;
1787
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)
1791   {
1792     CVariant currentType = CVariant(CVariant::VariantTypeObject);
1793     typeIterator->second->Print(false, true, true, printDescriptions, currentType);
1794
1795     result["types"][typeIterator->first] = currentType;
1796   }
1797
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++)
1802   {
1803     if ((clientPermissions & methodIterator->second.permission) != methodIterator->second.permission || ((transportCapabilities & methodIterator->second.transportneed) != methodIterator->second.transportneed && filterByTransport))
1804       continue;
1805
1806     CVariant currentMethod = CVariant(CVariant::VariantTypeObject);
1807
1808     currentMethod["type"] = "method";
1809     if (printDescriptions && !methodIterator->second.description.empty())
1810       currentMethod["description"] = methodIterator->second.description;
1811     if (printMetadata)
1812     {
1813       CVariant permissions(CVariant::VariantTypeArray);
1814       for (int i = ReadData; i <= OPERATION_PERMISSION_ALL; i *= 2)
1815       {
1816         if ((methodIterator->second.permission & i) == i)
1817           permissions.push_back(PermissionToString((OperationPermission)i));
1818       }
1819
1820       if (permissions.size() == 1)
1821         currentMethod["permission"] = permissions[0];
1822       else
1823         currentMethod["permission"] = permissions;
1824     }
1825
1826     currentMethod["params"] = CVariant(CVariant::VariantTypeArray);
1827     for (unsigned int paramIndex = 0; paramIndex < methodIterator->second.parameters.size(); paramIndex++)
1828     {
1829       CVariant param = CVariant(CVariant::VariantTypeObject);
1830       methodIterator->second.parameters.at(paramIndex)->Print(true, false, true, printDescriptions, param);
1831       currentMethod["params"].append(param);
1832     }
1833
1834     methodIterator->second.returns->Print(false, false, false, printDescriptions, currentMethod["returns"]);
1835
1836     result["methods"][methodIterator->second.name] = currentMethod;
1837   }
1838
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];
1844
1845   return OK;
1846 }
1847
1848 JSONRPC_STATUS CJSONServiceDescription::CheckCall(const char* const method, const CVariant &requestParameters, ITransportLayer *transport, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters)
1849 {
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);
1853
1854   return MethodNotFound;
1855 }
1856
1857 JSONSchemaTypeDefinitionPtr CJSONServiceDescription::GetType(const std::string &identification)
1858 {
1859   std::map<std::string, JSONSchemaTypeDefinitionPtr>::iterator iter = m_types.find(identification);
1860   if (iter == m_types.end())
1861     return JSONSchemaTypeDefinitionPtr();
1862   
1863   return iter->second;
1864 }
1865
1866 bool CJSONServiceDescription::parseJSONSchemaType(const CVariant &value, std::vector<JSONSchemaTypeDefinitionPtr>& typeDefinitions, JSONSchemaType &schemaType, std::string &missingReference)
1867 {
1868   missingReference.clear();
1869   schemaType = AnyValue;
1870
1871   if (value.isArray())
1872   {
1873     int parsedType = 0;
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++)
1877     {
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())
1883       {
1884         if (!definition->Parse(value[typeIndex]))
1885         {
1886           missingReference = definition->missingReference;
1887           CLog::Log(LOGERROR, "JSONRPC: Invalid type schema in union type definition");
1888           return false;
1889         }
1890       }
1891       else
1892       {
1893         CLog::Log(LOGWARNING, "JSONRPC: Invalid type in union type definition");
1894         return false;
1895       }
1896
1897       definition->optional = false;
1898       typeDefinitions.push_back(definition);
1899       parsedType |= definition->type;
1900     }
1901
1902     // If the type has not been set yet set it to "any"
1903     if (parsedType != 0)
1904       schemaType = (JSONSchemaType)parsedType;
1905
1906     return true;
1907   }
1908   
1909   if (value.isString())
1910   {
1911     schemaType = StringToSchemaValueType(value.asString());
1912     return true;
1913   }
1914   
1915   return false;
1916 }
1917
1918 void CJSONServiceDescription::addReferenceTypeDefinition(JSONSchemaTypeDefinitionPtr typeDefinition)
1919 {
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())
1923     return;
1924
1925   // If the id has already been defined we ignore the type definition
1926   if (m_types.find(typeDefinition->ID) != m_types.end())
1927     return;
1928
1929   // Add the type to the list of type definitions
1930   m_types[typeDefinition->ID] = typeDefinition;
1931   
1932   IncompleteSchemaDefinitionMap::iterator iter = m_incompleteDefinitions.find(typeDefinition->ID);
1933   if (iter == m_incompleteDefinitions.end())
1934     return;
1935     
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++)
1938   {
1939     if (iter->second[index].Type == SchemaDefinitionType)
1940       AddType(iter->second[index].Schema);
1941     else
1942       AddMethod(iter->second[index].Schema, iter->second[index].Method);
1943   }
1944   
1945   m_incompleteDefinitions.erase(typeDefinition->ID);
1946 }
1947
1948 void CJSONServiceDescription::removeReferenceTypeDefinition(const std::string &typeID)
1949 {
1950   if (typeID.empty())
1951     return;
1952
1953   map<string, JSONSchemaTypeDefinitionPtr>::iterator type = m_types.find(typeID);
1954   if (type != m_types.end())
1955     m_types.erase(type);
1956 }
1957
1958 void CJSONServiceDescription::getReferencedTypes(const JSONSchemaTypeDefinitionPtr type, std::vector<std::string> &referencedTypes)
1959 {
1960   // If the current type is a referenceable object, we can add it to the list
1961   if (type->ID.size() > 0)
1962   {
1963     for (unsigned int index = 0; index < referencedTypes.size(); index++)
1964     {
1965       // The referenceable object has already been added to the list so we can just skip it
1966       if (type->ID == referencedTypes.at(index))
1967         return;
1968     }
1969
1970     referencedTypes.push_back(type->ID);
1971   }
1972
1973   // If the current type is an object we need to check its properties
1974   if (HasType(type->type, ObjectValue))
1975   {
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);
1980   }
1981   // If the current type is an array we need to check its items
1982   if (HasType(type->type, ArrayValue))
1983   {
1984     unsigned int index;
1985     for (index = 0; index < type->items.size(); index++)
1986       getReferencedTypes(type->items.at(index), referencedTypes);
1987
1988     for (index = 0; index < type->additionalItems.size(); index++)
1989       getReferencedTypes(type->additionalItems.at(index), referencedTypes);
1990   }
1991
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);
1995
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);
1999 }
2000
2001 CJSONServiceDescription::CJsonRpcMethodMap::CJsonRpcMethodMap()
2002 {
2003   m_actionmap = std::map<std::string, JsonRpcMethod>();
2004 }
2005
2006 void CJSONServiceDescription::CJsonRpcMethodMap::clear()
2007 {
2008   m_actionmap.clear();
2009 }
2010
2011 void CJSONServiceDescription::CJsonRpcMethodMap::add(const JsonRpcMethod &method)
2012 {
2013   CStdString name = method.name;
2014   name = name.ToLower();
2015   m_actionmap[name] = method;
2016 }
2017
2018 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::begin() const
2019 {
2020   return m_actionmap.begin();
2021 }
2022
2023 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::find(const std::string& key) const
2024 {
2025   return m_actionmap.find(key);
2026 }
2027
2028 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::end() const
2029 {
2030   return m_actionmap.end();
2031 }