jsonrpc: fix handling of default value for arrays
[xbmc:paulepanters-xbmc.git] / xbmc / interfaces / json-rpc / JSONServiceDescription.cpp
1 /*
2  *      Copyright (C) 2005-2010 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include <limits>
23 #include "ServiceDescription.h"
24 #include "JSONServiceDescription.h"
25 #include "utils/log.h"
26 #include "utils/StdString.h"
27 #include "utils/JSONVariantParser.h"
28 #include "JSONRPC.h"
29 #include "PlayerOperations.h"
30 #include "AVPlayerOperations.h"
31 #include "PicturePlayerOperations.h"
32 #include "AVPlaylistOperations.h"
33 #include "PlaylistOperations.h"
34 #include "FileOperations.h"
35 #include "AudioLibrary.h"
36 #include "VideoLibrary.h"
37 #include "SystemOperations.h"
38 #include "InputOperations.h"
39 #include "XBMCOperations.h"
40 #include "ApplicationOperations.h"
41
42 using namespace std;
43 using namespace JSONRPC;
44
45 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::CJsonSchemaPropertiesMap()
46 {
47   m_propertiesmap = std::map<std::string, JSONSchemaTypeDefinition>();
48 }
49
50 void JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::add(JSONSchemaTypeDefinition &property)
51 {
52   CStdString name = property.name;
53   name = name.ToLower();
54   m_propertiesmap[name] = property;
55 }
56
57 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::begin() const
58 {
59   return m_propertiesmap.begin();
60 }
61
62 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::find(const std::string& key) const
63 {
64   return m_propertiesmap.find(key);
65 }
66
67 JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::end() const
68 {
69   return m_propertiesmap.end();
70 }
71
72 unsigned int JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::size() const
73 {
74   return m_propertiesmap.size();
75 }
76
77 std::map<std::string, CVariant> CJSONServiceDescription::m_notifications = std::map<std::string, CVariant>();
78 CJSONServiceDescription::CJsonRpcMethodMap CJSONServiceDescription::m_actionMap;
79 std::map<std::string, JSONSchemaTypeDefinition> CJSONServiceDescription::m_types = std::map<std::string, JSONSchemaTypeDefinition>();
80
81 JsonRpcMethodMap CJSONServiceDescription::m_methodMaps[] = {
82 // JSON-RPC
83   { "JSONRPC.Introspect",                           CJSONRPC::Introspect },
84   { "JSONRPC.Version",                              CJSONRPC::Version },
85   { "JSONRPC.Permission",                           CJSONRPC::Permission },
86   { "JSONRPC.Ping",                                 CJSONRPC::Ping },
87   { "JSONRPC.GetConfiguration",                     CJSONRPC::GetConfiguration },
88   { "JSONRPC.SetConfiguration",                     CJSONRPC::SetConfiguration },
89   { "JSONRPC.NotifyAll",                            CJSONRPC::NotifyAll },
90
91 // Player
92   { "Player.GetActivePlayers",                      CPlayerOperations::GetActivePlayers },
93
94 // Music player
95   { "AudioPlayer.State",                            CAVPlayerOperations::State },
96   { "AudioPlayer.PlayPause",                        CAVPlayerOperations::PlayPause },
97   { "AudioPlayer.Stop",                             CAVPlayerOperations::Stop },
98   { "AudioPlayer.SkipPrevious",                     CAVPlayerOperations::SkipPrevious },
99   { "AudioPlayer.SkipNext",                         CAVPlayerOperations::SkipNext },
100
101   { "AudioPlayer.BigSkipBackward",                  CAVPlayerOperations::BigSkipBackward },
102   { "AudioPlayer.BigSkipForward",                   CAVPlayerOperations::BigSkipForward },
103   { "AudioPlayer.SmallSkipBackward",                CAVPlayerOperations::SmallSkipBackward },
104   { "AudioPlayer.SmallSkipForward",                 CAVPlayerOperations::SmallSkipForward },
105
106   { "AudioPlayer.Rewind",                           CAVPlayerOperations::Rewind },
107   { "AudioPlayer.Forward",                          CAVPlayerOperations::Forward },
108
109   { "AudioPlayer.GetTime",                          CAVPlayerOperations::GetTime },
110   { "AudioPlayer.GetPercentage",                    CAVPlayerOperations::GetPercentage },
111   { "AudioPlayer.SeekTime",                         CAVPlayerOperations::SeekTime },
112   { "AudioPlayer.SeekPercentage",                   CAVPlayerOperations::SeekPercentage },
113
114 // Video player
115   { "VideoPlayer.State",                            CAVPlayerOperations::State },
116   { "VideoPlayer.PlayPause",                        CAVPlayerOperations::PlayPause },
117   { "VideoPlayer.Stop",                             CAVPlayerOperations::Stop },
118   { "VideoPlayer.SkipPrevious",                     CAVPlayerOperations::SkipPrevious },
119   { "VideoPlayer.SkipNext",                         CAVPlayerOperations::SkipNext },
120
121   { "VideoPlayer.BigSkipBackward",                  CAVPlayerOperations::BigSkipBackward },
122   { "VideoPlayer.BigSkipForward",                   CAVPlayerOperations::BigSkipForward },
123   { "VideoPlayer.SmallSkipBackward",                CAVPlayerOperations::SmallSkipBackward },
124   { "VideoPlayer.SmallSkipForward",                 CAVPlayerOperations::SmallSkipForward },
125
126   { "VideoPlayer.Rewind",                           CAVPlayerOperations::Rewind },
127   { "VideoPlayer.Forward",                          CAVPlayerOperations::Forward },
128
129   { "VideoPlayer.GetTime",                          CAVPlayerOperations::GetTime },
130   { "VideoPlayer.GetPercentage",                    CAVPlayerOperations::GetPercentage },
131   { "VideoPlayer.SeekTime",                         CAVPlayerOperations::SeekTime },
132   { "VideoPlayer.SeekPercentage",                   CAVPlayerOperations::SeekPercentage },
133
134 // Picture player
135   { "PicturePlayer.PlayPause",                      CPicturePlayerOperations::PlayPause },
136   { "PicturePlayer.Stop",                           CPicturePlayerOperations::Stop },
137   { "PicturePlayer.SkipPrevious",                   CPicturePlayerOperations::SkipPrevious },
138   { "PicturePlayer.SkipNext",                       CPicturePlayerOperations::SkipNext },
139
140   { "PicturePlayer.MoveLeft",                       CPicturePlayerOperations::MoveLeft },
141   { "PicturePlayer.MoveRight",                      CPicturePlayerOperations::MoveRight },
142   { "PicturePlayer.MoveDown",                       CPicturePlayerOperations::MoveDown },
143   { "PicturePlayer.MoveUp",                         CPicturePlayerOperations::MoveUp },
144
145   { "PicturePlayer.ZoomOut",                        CPicturePlayerOperations::ZoomOut },
146   { "PicturePlayer.ZoomIn",                         CPicturePlayerOperations::ZoomIn },
147   { "PicturePlayer.Zoom",                           CPicturePlayerOperations::Zoom },
148   { "PicturePlayer.Rotate",                         CPicturePlayerOperations::Rotate },
149
150 // Video Playlist
151   { "VideoPlaylist.State",                          CAVPlaylistOperations::State },
152   { "VideoPlaylist.Play",                           CAVPlaylistOperations::Play },
153   { "VideoPlaylist.SkipPrevious",                   CAVPlaylistOperations::SkipPrevious },
154   { "VideoPlaylist.SkipNext",                       CAVPlaylistOperations::SkipNext },
155   { "VideoPlaylist.GetItems",                       CAVPlaylistOperations::GetItems },
156   { "VideoPlaylist.Add",                            CAVPlaylistOperations::Add },
157   { "VideoPlaylist.Insert",                         CAVPlaylistOperations::Insert },
158   { "VideoPlaylist.Clear",                          CAVPlaylistOperations::Clear },
159   { "VideoPlaylist.Shuffle",                        CAVPlaylistOperations::Shuffle },
160   { "VideoPlaylist.UnShuffle",                      CAVPlaylistOperations::UnShuffle },
161   { "VideoPlaylist.Repeat",                         CAVPlaylistOperations::Repeat },
162   { "VideoPlaylist.Remove",                         CAVPlaylistOperations::Remove },
163   { "VideoPlaylist.Swap",                           CAVPlaylistOperations::Swap },
164
165 // AudioPlaylist
166   { "AudioPlaylist.State",                          CAVPlaylistOperations::State },
167   { "AudioPlaylist.Play",                           CAVPlaylistOperations::Play },
168   { "AudioPlaylist.SkipPrevious",                   CAVPlaylistOperations::SkipPrevious },
169   { "AudioPlaylist.SkipNext",                       CAVPlaylistOperations::SkipNext },
170   { "AudioPlaylist.GetItems",                       CAVPlaylistOperations::GetItems },
171   { "AudioPlaylist.Add",                            CAVPlaylistOperations::Add },
172   { "AudioPlaylist.Insert",                         CAVPlaylistOperations::Insert },
173   { "AudioPlaylist.Clear",                          CAVPlaylistOperations::Clear },
174   { "AudioPlaylist.Shuffle",                        CAVPlaylistOperations::Shuffle },
175   { "AudioPlaylist.UnShuffle",                      CAVPlaylistOperations::UnShuffle },
176   { "AudioPlaylist.Repeat",                         CAVPlaylistOperations::Repeat },
177   { "AudioPlaylist.Remove",                         CAVPlaylistOperations::Remove },
178   { "AudioPlaylist.Swap",                           CAVPlaylistOperations::Swap },
179
180 // Playlist
181   { "Playlist.Create",                              CPlaylistOperations::Create },
182   { "Playlist.Destroy",                             CPlaylistOperations::Destroy },
183
184   { "Playlist.GetItems",                            CPlaylistOperations::GetItems },
185   { "Playlist.Add",                                 CPlaylistOperations::Add },
186   { "Playlist.Remove",                              CPlaylistOperations::Remove },
187   { "Playlist.Swap",                                CPlaylistOperations::Swap },
188   { "Playlist.Clear",                               CPlaylistOperations::Clear },
189   { "Playlist.Shuffle",                             CPlaylistOperations::Shuffle },
190   { "Playlist.UnShuffle",                           CPlaylistOperations::UnShuffle },
191
192 // Files
193   { "Files.GetSources",                             CFileOperations::GetRootDirectory },
194   { "Files.Download",                               CFileOperations::Download },
195   { "Files.GetDirectory",                           CFileOperations::GetDirectory },
196
197 // Music Library
198   { "AudioLibrary.GetArtists",                      CAudioLibrary::GetArtists },
199   { "AudioLibrary.GetArtistDetails",                CAudioLibrary::GetArtistDetails },
200   { "AudioLibrary.GetAlbums",                       CAudioLibrary::GetAlbums },
201   { "AudioLibrary.GetAlbumDetails",                 CAudioLibrary::GetAlbumDetails },
202   { "AudioLibrary.GetSongs",                        CAudioLibrary::GetSongs },
203   { "AudioLibrary.GetSongDetails",                  CAudioLibrary::GetSongDetails },
204   { "AudioLibrary.GetRecentlyAddedAlbums",          CAudioLibrary::GetRecentlyAddedAlbums },
205   { "AudioLibrary.GetRecentlyAddedSongs",           CAudioLibrary::GetRecentlyAddedSongs },
206   { "AudioLibrary.GetGenres",                       CAudioLibrary::GetGenres },
207   { "AudioLibrary.Scan",                            CAudioLibrary::Scan },
208   { "AudioLibrary.Export",                          CAudioLibrary::Export },
209   { "AudioLibrary.Clean",                           CAudioLibrary::Clean },
210
211 // Video Library
212   { "VideoLibrary.GetGenres",                       CVideoLibrary::GetGenres },
213   { "VideoLibrary.GetMovies",                       CVideoLibrary::GetMovies },
214   { "VideoLibrary.GetMovieDetails",                 CVideoLibrary::GetMovieDetails },
215   { "VideoLibrary.GetMovieSets",                    CVideoLibrary::GetMovieSets },
216   { "VideoLibrary.GetMovieSetDetails",              CVideoLibrary::GetMovieSetDetails },
217   { "VideoLibrary.GetTVShows",                      CVideoLibrary::GetTVShows },
218   { "VideoLibrary.GetTVShowDetails",                CVideoLibrary::GetTVShowDetails },
219   { "VideoLibrary.GetSeasons",                      CVideoLibrary::GetSeasons },
220   { "VideoLibrary.GetEpisodes",                     CVideoLibrary::GetEpisodes },
221   { "VideoLibrary.GetEpisodeDetails",               CVideoLibrary::GetEpisodeDetails },
222   { "VideoLibrary.GetMusicVideos",                  CVideoLibrary::GetMusicVideos },
223   { "VideoLibrary.GetMusicVideoDetails",            CVideoLibrary::GetMusicVideoDetails },
224   { "VideoLibrary.GetRecentlyAddedMovies",          CVideoLibrary::GetRecentlyAddedMovies },
225   { "VideoLibrary.GetRecentlyAddedEpisodes",        CVideoLibrary::GetRecentlyAddedEpisodes },
226   { "VideoLibrary.GetRecentlyAddedMusicVideos",     CVideoLibrary::GetRecentlyAddedMusicVideos },
227   { "VideoLibrary.Scan",                            CVideoLibrary::Scan },
228   { "VideoLibrary.Export",                          CVideoLibrary::Export },
229   { "VideoLibrary.Clean",                           CVideoLibrary::Clean },
230
231 // System operations
232   { "System.GetProperties",                         CSystemOperations::GetProperties },
233   { "System.Shutdown",                              CSystemOperations::Shutdown },
234   { "System.Suspend",                               CSystemOperations::Suspend },
235   { "System.Hibernate",                             CSystemOperations::Hibernate },
236   { "System.Reboot",                                CSystemOperations::Reboot },
237
238 // Input operations
239   { "Input.Left",                                   CInputOperations::Left },
240   { "Input.Right",                                  CInputOperations::Right },
241   { "Input.Down",                                   CInputOperations::Down },
242   { "Input.Up",                                     CInputOperations::Up },
243   { "Input.Select",                                 CInputOperations::Select },
244   { "Input.Back",                                   CInputOperations::Back },
245   { "Input.Home",                                   CInputOperations::Home },
246
247 // Application operations
248   { "Application.GetProperties",                    CApplicationOperations::GetProperties },
249   { "Application.SetVolume",                        CApplicationOperations::SetVolume },
250   { "Application.ToggleMute",                       CApplicationOperations::ToggleMute },
251   { "Application.Quit",                             CApplicationOperations::Quit },
252
253 // XBMC operations
254   { "XBMC.Play",                                    CXBMCOperations::Play },
255   { "XBMC.StartSlideshow",                          CXBMCOperations::StartSlideshow },
256   { "XBMC.GetInfoLabels",                           CXBMCOperations::GetInfoLabels },
257   { "XBMC.GetInfoBooleans",                         CXBMCOperations::GetInfoBooleans }
258 };
259
260 bool CJSONServiceDescription::prepareDescription(std::string &description, CVariant &descriptionObject, std::string &name)
261 {
262   if (description.empty())
263   {
264     CLog::Log(LOGERROR, "JSONRPC: Missing JSON Schema definition for \"%s\"", name.c_str());
265     return false;
266   }
267
268   if (description.at(0) != '{')
269   {
270     CStdString json;
271     json.Format("{%s}", description);
272     description = json;
273   }
274
275   descriptionObject = CJSONVariantParser::Parse((const unsigned char *)description.c_str(), description.size());
276
277   // Make sure the method description actually exists and represents an object
278   if (!descriptionObject.isObject())
279   {
280     CLog::Log(LOGERROR, "JSONRPC: Unable to parse JSON Schema definition for \"%s\"", name.c_str());
281     return false;
282   }
283
284   CVariant::const_iterator_map member = descriptionObject.begin_map();
285   if (member != descriptionObject.end_map())
286     name = member->first;
287
288   if (name.empty() ||
289      (!descriptionObject[name].isMember("type") && !descriptionObject[name].isMember("$ref") && !descriptionObject[name].isMember("extends")))
290   {
291     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for \"%s\"", name.c_str());
292     return false;
293   }
294
295   return true;
296 }
297
298 bool CJSONServiceDescription::addMethod(std::string &jsonMethod, MethodCall method)
299 {
300   CVariant descriptionObject;
301   std::string methodName;
302
303   // Make sure the method description actually exists and represents an object
304   if (!prepareDescription(jsonMethod, descriptionObject, methodName))
305   {
306     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for method \"%s\"", methodName.c_str());
307     return false;
308   }
309
310   if (m_actionMap.find(methodName) != m_actionMap.end())
311   {
312     CLog::Log(LOGERROR, "JSONRPC: There already is a method with the name \"%s\"", methodName.c_str());
313     return false;
314   }
315
316   std::string type = GetString(descriptionObject[methodName]["type"], "");
317   if (type.compare("method") != 0)
318   {
319     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for method \"%s\"", methodName.c_str());
320     return false;
321   }
322
323   if (method == NULL)
324   {
325     unsigned int size = sizeof(m_methodMaps) / sizeof(JsonRpcMethodMap);
326     for (unsigned int index = 0; index < size; index++)
327     {
328       if (methodName.compare(m_methodMaps[index].name) == 0)
329       {
330         method = m_methodMaps[index].method;
331         break;
332       }
333     }
334
335     if (method == NULL)
336     {
337       CLog::Log(LOGERROR, "JSONRPC: Missing implementation for method \"%s\"", methodName.c_str());
338       return false;
339     }
340   }
341
342   // Parse the details of the method
343   JsonRpcMethod newMethod;
344   newMethod.name = methodName;
345   newMethod.method = method;
346   if (!parseMethod(descriptionObject[newMethod.name], newMethod))
347   {
348     CLog::Log(LOGERROR, "JSONRPC: Could not parse method \"%s\"", methodName.c_str());
349     return false;
350   }
351
352   m_actionMap.add(newMethod);
353
354   return true;
355 }
356
357 bool CJSONServiceDescription::AddType(std::string jsonType)
358 {
359   CVariant descriptionObject;
360   std::string typeName;
361
362   if (!prepareDescription(jsonType, descriptionObject, typeName))
363   {
364     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for type \"%s\"", typeName.c_str());
365     return false;
366   }
367
368   if (m_types.find(typeName) != m_types.end())
369   {
370     CLog::Log(LOGERROR, "JSONRPC: There already is a type with the name \"%s\"", typeName.c_str());
371     return false;
372   }
373
374   // Make sure the "id" attribute is correctly populated
375   descriptionObject[typeName]["id"] = typeName;
376
377   JSONSchemaTypeDefinition globalType;
378   globalType.name = typeName;
379
380   if (!parseTypeDefinition(descriptionObject[typeName], globalType, false))
381   {
382     CLog::Log(LOGERROR, "JSONRPC: Could not parse type \"%s\"", typeName.c_str());
383     return false;
384   }
385
386   return true;
387 }
388
389 bool CJSONServiceDescription::AddMethod(std::string jsonMethod, MethodCall method)
390 {
391   if (method == NULL)
392   {
393     CLog::Log(LOGERROR, "JSONRPC: Invalid JSONRPC method implementation");
394     return false;
395   }
396
397   return addMethod(jsonMethod, method);
398 }
399
400 bool CJSONServiceDescription::AddBuiltinMethod(std::string jsonMethod)
401 {
402   return addMethod(jsonMethod, NULL);
403 }
404
405 bool CJSONServiceDescription::AddNotification(std::string jsonNotification)
406 {
407   CVariant descriptionObject;
408   std::string notificationName;
409
410   // Make sure the notification description actually exists and represents an object
411   if (!prepareDescription(jsonNotification, descriptionObject, notificationName))
412   {
413     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON Schema definition for notification \"%s\"", notificationName.c_str());
414     return false;
415   }
416
417   if (m_notifications.find(notificationName) != m_notifications.end())
418   {
419     CLog::Log(LOGERROR, "JSONRPC: There already is a notification with the name \"%s\"", notificationName.c_str());
420     return false;
421   }
422
423   std::string type = GetString(descriptionObject[notificationName]["type"], "");
424   if (type.compare("notification") != 0)
425   {
426     CLog::Log(LOGERROR, "JSONRPC: Invalid JSON type for notification \"%s\"", notificationName.c_str());
427     return false;
428   }
429
430   m_notifications[notificationName] = descriptionObject;
431
432   return true;
433 }
434
435 int CJSONServiceDescription::GetVersion()
436 {
437   return JSONRPC_SERVICE_VERSION;
438 }
439
440 JSON_STATUS CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport, IClient *client,
441   bool printDescriptions /* = true */, bool printMetadata /* = false */, bool filterByTransport /* = true */,
442   std::string filterByName /* = "" */, std::string filterByType /* = "" */, bool printReferences /* = true */)
443 {
444   std::map<std::string, JSONSchemaTypeDefinition> types;
445   CJsonRpcMethodMap methods;
446   std::map<std::string, CVariant> notifications;
447
448   int clientPermissions = client->GetPermissionFlags();
449   int transportCapabilities = transport->GetCapabilities();
450
451   if (filterByName.size() > 0)
452   {
453     CStdString name = filterByName;
454
455     if (filterByType == "method")
456     {
457       name = name.ToLower();
458
459       CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator = m_actionMap.find(name);
460       if (methodIterator != m_actionMap.end() &&
461          (clientPermissions & methodIterator->second.permission) == methodIterator->second.permission && ((transportCapabilities & methodIterator->second.transportneed) == methodIterator->second.transportneed || !filterByTransport))
462         methods.add(methodIterator->second);
463       else
464         return InvalidParams;
465     }
466     else if (filterByType == "namespace")
467     {
468       // append a . delimiter to make sure we check for a namespace
469       name = name.ToLower().append(".");
470
471       CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
472       CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = m_actionMap.end();
473       for (methodIterator = m_actionMap.begin(); methodIterator != methodIteratorEnd; methodIterator++)
474       {
475         // Check if the given name is at the very beginning of the method name
476         if (methodIterator->first.find(name) == 0 &&
477            (clientPermissions & methodIterator->second.permission) == methodIterator->second.permission && ((transportCapabilities & methodIterator->second.transportneed) == methodIterator->second.transportneed || !filterByTransport))
478           methods.add(methodIterator->second);
479       }
480
481       if (methods.begin() == methods.end())
482         return InvalidParams;
483     }
484     else if (filterByType == "type")
485     {
486       std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator = m_types.find(name);
487       if (typeIterator != m_types.end())
488         types[typeIterator->first] = typeIterator->second;
489       else
490         return InvalidParams;
491     }
492     else if (filterByType == "notification")
493     {
494       std::map<std::string, CVariant>::const_iterator notificationIterator = m_notifications.find(name);
495       if (notificationIterator != m_notifications.end())
496         notifications[notificationIterator->first] = notificationIterator->second;
497       else
498         return InvalidParams;
499     }
500     else
501       return InvalidParams;
502
503     // If we need to print all referenced types we have to go through all parameters etc
504     if (printReferences)
505     {
506       std::vector<std::string> referencedTypes;
507
508       // Loop through all printed types to get all referenced types
509       std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator;
510       std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIteratorEnd = types.end();
511       for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; typeIterator++)
512         getReferencedTypes(typeIterator->second, referencedTypes);
513
514       // Loop through all printed method's parameters and return value to get all referenced types
515       CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
516       CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end();
517       for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++)
518       {
519         for (unsigned int index = 0; index < methodIterator->second.parameters.size(); index++)
520           getReferencedTypes(methodIterator->second.parameters.at(index), referencedTypes);
521
522         getReferencedTypes(methodIterator->second.returns, referencedTypes);
523       }
524
525       for (unsigned int index = 0; index < referencedTypes.size(); index++)
526       {
527         std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator = m_types.find(referencedTypes.at(index));
528         if (typeIterator != m_types.end())
529           types[typeIterator->first] = typeIterator->second;
530       }
531     }
532   }
533   else
534   {
535     types = m_types;
536     methods = m_actionMap;
537     notifications = m_notifications;
538   }
539
540   // Print the header
541   result["id"] = JSONRPC_SERVICE_ID;
542   result["version"] = JSONRPC_SERVICE_VERSION;
543   result["description"] = JSONRPC_SERVICE_DESCRIPTION;
544
545   std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator;
546   std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIteratorEnd = types.end();
547   for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; typeIterator++)
548   {
549     CVariant currentType = CVariant(CVariant::VariantTypeObject);
550     printType(typeIterator->second, false, true, true, printDescriptions, currentType);
551
552     result["types"][typeIterator->first] = currentType;
553   }
554
555   // Iterate through all json rpc methods
556   CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator;
557   CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end();
558   for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++)
559   {
560     if ((clientPermissions & methodIterator->second.permission) != methodIterator->second.permission || ((transportCapabilities & methodIterator->second.transportneed) != methodIterator->second.transportneed && filterByTransport))
561       continue;
562
563     CVariant currentMethod = CVariant(CVariant::VariantTypeObject);
564
565     currentMethod["type"] = "method";
566     if (printDescriptions && !methodIterator->second.description.empty())
567       currentMethod["description"] = methodIterator->second.description;
568     if (printMetadata)
569     {
570       CVariant permissions(CVariant::VariantTypeArray);
571       for (int i = ReadData; i <= OPERATION_PERMISSION_ALL; i *= 2)
572       {
573         if ((methodIterator->second.permission & i) == i)
574           permissions.push_back(PermissionToString((OperationPermission)i));
575       }
576
577       if (permissions.size() == 1)
578         currentMethod["permission"] = permissions[0];
579       else
580         currentMethod["permission"] = permissions;
581     }
582
583     currentMethod["params"] = CVariant(CVariant::VariantTypeArray);
584     for (unsigned int paramIndex = 0; paramIndex < methodIterator->second.parameters.size(); paramIndex++)
585     {
586       CVariant param = CVariant(CVariant::VariantTypeObject);
587       printType(methodIterator->second.parameters.at(paramIndex), true, false, true, printDescriptions, param);
588       currentMethod["params"].append(param);
589     }
590
591     printType(methodIterator->second.returns, false, false, false, printDescriptions, currentMethod["returns"]);
592
593     result["methods"][methodIterator->second.name] = currentMethod;
594   }
595
596   // Print notification description
597   std::map<std::string, CVariant>::const_iterator notificationIterator;
598   std::map<std::string, CVariant>::const_iterator notificationIteratorEnd = notifications.end();
599   for (notificationIterator = notifications.begin(); notificationIterator != notificationIteratorEnd; notificationIterator++)
600     result["notifications"][notificationIterator->first] = notificationIterator->second[notificationIterator->first];
601
602   return OK;
603 }
604
605 JSON_STATUS CJSONServiceDescription::CheckCall(const char* const method, const CVariant &requestParameters, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters)
606 {
607   CJsonRpcMethodMap::JsonRpcMethodIterator iter = m_actionMap.find(method);
608   if (iter != m_actionMap.end())
609   {
610     if (client != NULL && (client->GetPermissionFlags() & iter->second.permission) == iter->second.permission && (!notification || (iter->second.permission & OPERATION_PERMISSION_NOTIFICATION) == iter->second.permission))
611     {
612       methodCall = iter->second.method;
613
614       // Count the number of actually handled (present)
615       // parameters
616       unsigned int handled = 0;
617       CVariant errorData = CVariant(CVariant::VariantTypeObject);
618       errorData["method"] = iter->second.name;
619
620       // Loop through all the parameters to check
621       for (unsigned int i = 0; i < iter->second.parameters.size(); i++)
622       {
623         // Evaluate the current parameter
624         JSON_STATUS status = checkParameter(requestParameters, iter->second.parameters.at(i), i, outputParameters, handled, errorData);
625         if (status != OK)
626         {
627           // Return the error data object in the outputParameters reference
628           outputParameters = errorData;
629           return status;
630         }
631       }
632
633       // Check if there were unnecessary parameters
634       if (handled < requestParameters.size())
635       {
636         errorData["message"] = "Too many parameters";
637         outputParameters = errorData;
638         return InvalidParams;
639       }
640
641       return OK;
642     }
643     else
644       return BadPermission;
645   }
646   else
647     return MethodNotFound;
648 }
649
650 void CJSONServiceDescription::printType(const JSONSchemaTypeDefinition &type, bool isParameter, bool isGlobal, bool printDefault, bool printDescriptions, CVariant &output)
651 {
652   bool typeReference = false;
653
654   // Printing general fields
655   if (isParameter)
656     output["name"] = type.name;
657
658   if (isGlobal)
659     output["id"] = type.ID;
660   else if (!type.ID.empty())
661   {
662     output["$ref"] = type.ID;
663     typeReference = true;
664   }
665
666   if (printDescriptions && !type.description.empty())
667     output["description"] = type.description;
668
669   if (isParameter || printDefault)
670   {
671     if (!type.optional)
672       output["required"] = true;
673     if (type.optional && type.type != ObjectValue && type.type != ArrayValue)
674       output["default"] = type.defaultValue;
675   }
676
677   if (!typeReference)
678   {
679     if (type.extends.size() == 1)
680     {
681       output["extends"] = type.extends.at(0).ID;
682     }
683     else if (type.extends.size() > 1)
684     {
685       output["extends"] = CVariant(CVariant::VariantTypeArray);
686       for (unsigned int extendsIndex = 0; extendsIndex < type.extends.size(); extendsIndex++)
687         output["extends"].append(type.extends.at(extendsIndex).ID);
688     }
689     else
690       SchemaValueTypeToJson(type.type, output["type"]);
691
692     // Printing enum field
693     if (type.enums.size() > 0)
694     {
695       output["enums"] = CVariant(CVariant::VariantTypeArray);
696       for (unsigned int enumIndex = 0; enumIndex < type.enums.size(); enumIndex++)
697         output["enums"].append(type.enums.at(enumIndex));
698     }
699
700     // Printing integer/number fields
701     if (HasType(type.type, IntegerValue) || HasType(type.type, NumberValue))
702     {
703       if (HasType(type.type, NumberValue))
704       {
705         if (type.minimum > -numeric_limits<double>::max())
706           output["minimum"] = type.minimum;
707         if (type.maximum < numeric_limits<double>::max())
708           output["maximum"] = type.maximum;
709       }
710       else
711       {
712         if (type.minimum > numeric_limits<int>::min())
713           output["minimum"] = (int)type.minimum;
714         if (type.maximum < numeric_limits<int>::max())
715           output["maximum"] = (int)type.maximum;
716       }
717
718       if (type.exclusiveMinimum)
719         output["exclusiveMinimum"] = true;
720       if (type.exclusiveMaximum)
721         output["exclusiveMaximum"] = true;
722       if (type.divisibleBy > 0)
723         output["divisibleBy"] = type.divisibleBy;
724     }
725     if (HasType(type.type, StringValue))
726     {
727       if (type.minLength >= 0)
728         output["minLength"] = type.minLength;
729       if (type.maxLength >= 0)
730         output["maxLength"] = type.maxLength;
731     }
732
733     // Print array fields
734     if (HasType(type.type, ArrayValue))
735     {
736       if (type.items.size() == 1)
737       {
738         printType(type.items.at(0), false, false, false, printDescriptions, output["items"]);
739       }
740       else if (type.items.size() > 1)
741       {
742         output["items"] = CVariant(CVariant::VariantTypeArray);
743         for (unsigned int itemIndex = 0; itemIndex < type.items.size(); itemIndex++)
744         {
745           CVariant item = CVariant(CVariant::VariantTypeObject);
746           printType(type.items.at(itemIndex), false, false, false, printDescriptions, item);
747           output["items"].append(item);
748         }
749       }
750
751       if (type.minItems > 0)
752         output["minItems"] = type.minItems;
753       if (type.maxItems > 0)
754         output["maxItems"] = type.maxItems;
755
756       if (type.additionalItems.size() == 1)
757       {
758         printType(type.additionalItems.at(0), false, false, false, printDescriptions, output["additionalItems"]);
759       }
760       else if (type.additionalItems.size() > 1)
761       {
762         output["additionalItems"] = CVariant(CVariant::VariantTypeArray);
763         for (unsigned int addItemIndex = 0; addItemIndex < type.additionalItems.size(); addItemIndex++)
764         {
765           CVariant item = CVariant(CVariant::VariantTypeObject);
766           printType(type.additionalItems.at(addItemIndex), false, false, false, printDescriptions, item);
767           output["additionalItems"].append(item);
768         }
769       }
770
771       if (type.uniqueItems)
772         output["uniqueItems"] = true;
773     }
774
775     // Print object fields
776     if (HasType(type.type, ObjectValue) && type.properties.size() > 0)
777     {
778       output["properties"] = CVariant(CVariant::VariantTypeObject);
779
780       JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesEnd = type.properties.end();
781       JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesIterator;
782       for (propertiesIterator = type.properties.begin(); propertiesIterator != propertiesEnd; propertiesIterator++)
783       {
784         printType(propertiesIterator->second, false, false, true, printDescriptions, output["properties"][propertiesIterator->first]);
785       }
786
787       if (!type.hasAdditionalProperties)
788         output["additionalProperties"] = false;
789       else if (type.additionalProperties != NULL && type.additionalProperties->type != AnyValue)
790         printType(*(type.additionalProperties), false, false, true, printDescriptions, output["additionalProperties"]);
791     }
792   }
793 }
794
795 JSON_STATUS CJSONServiceDescription::checkParameter(const CVariant &requestParameters, const JSONSchemaTypeDefinition &type, unsigned int position, CVariant &outputParameters, unsigned int &handled, CVariant &errorData)
796 {
797   // Let's check if the parameter has been provided
798   if (ParameterExists(requestParameters, type.name, position))
799   {
800     // Get the parameter
801     CVariant parameterValue = GetParameter(requestParameters, type.name, position);
802
803     // Evaluate the type of the parameter
804     JSON_STATUS status = checkType(parameterValue, type, outputParameters[type.name], errorData["stack"]);
805     if (status != OK)
806       return status;
807
808     // The parameter was present and valid
809     handled++;
810   }
811   // If the parameter has not been provided but is optional
812   // we can use its default value
813   else if (type.optional)
814     outputParameters[type.name] = type.defaultValue;
815   // The parameter is required but has not been provided => invalid
816   else
817   {
818     errorData["stack"]["name"] = type.name;
819     SchemaValueTypeToJson(type.type, errorData["stack"]["type"]);
820     errorData["stack"]["message"] = "Missing parameter";
821     return InvalidParams;
822   }
823
824   return OK;
825 }
826
827 JSON_STATUS CJSONServiceDescription::checkType(const CVariant &value, const JSONSchemaTypeDefinition &type, CVariant &outputValue, CVariant &errorData)
828 {
829   if (!type.name.empty())
830     errorData["name"] = type.name;
831   SchemaValueTypeToJson(type.type, errorData["type"]);
832   CStdString errorMessage;
833
834   // Let's check the type of the provided parameter
835   if (!IsType(value, type.type))
836   {
837     CLog::Log(LOGWARNING, "JSONRPC: Type mismatch in type %s", type.name.c_str());
838     errorMessage.Format("Invalid type %s received", ValueTypeToString(value.type()));
839     errorData["message"] = errorMessage.c_str();
840     return InvalidParams;
841   }
842   else if (value.isNull() && !HasType(type.type, NullValue))
843   {
844     CLog::Log(LOGWARNING, "JSONRPC: Value is NULL in type %s", type.name.c_str());
845     errorData["message"] = "Received value is null";
846     return InvalidParams;
847   }
848
849   // First we need to check if this type extends another
850   // type and if so we need to check against the extended
851   // type first
852   if (type.extends.size() > 0)
853   {
854     for (unsigned int extendsIndex = 0; extendsIndex < type.extends.size(); extendsIndex++)
855     {
856       JSON_STATUS status = checkType(value, type.extends.at(extendsIndex), outputValue, errorData);
857
858       if (status != OK)
859       {
860         CLog::Log(LOGWARNING, "JSONRPC: Value does not match extended type %s of type %s", type.extends.at(extendsIndex).ID.c_str(), type.name.c_str());
861         errorMessage.Format("value does not match extended type %s", type.extends.at(extendsIndex).ID.c_str(), type.name.c_str());
862         errorData["message"] = errorMessage.c_str();
863         return status;
864       }
865     }
866   }
867
868   // If it is an array we need to
869   // - check the type of every element ("items")
870   // - check if they need to be unique ("uniqueItems")
871   if (HasType(type.type, ArrayValue) && value.isArray())
872   {
873     outputValue = CVariant(CVariant::VariantTypeArray);
874     // Check the number of items against minItems and maxItems
875     if ((type.minItems > 0 && value.size() < type.minItems) || (type.maxItems > 0 && value.size() > type.maxItems))
876     {
877       CLog::Log(LOGWARNING, "JSONRPC: Number of array elements does not match minItems and/or maxItems in type %s", type.name.c_str());
878       if (type.minItems > 0 && type.maxItems > 0)
879         errorMessage.Format("Between %d and %d array items expected but %d received", type.minItems, type.maxItems, value.size());
880       else if (type.minItems > 0)
881         errorMessage.Format("At least %d array items expected but only %d received", type.minItems, value.size());
882       else
883         errorMessage.Format("Only %d array items expected but %d received", type.maxItems, value.size());
884       errorData["message"] = errorMessage.c_str();
885       return InvalidParams;
886     }
887
888     if (type.items.size() == 0)
889     {
890       outputValue = value;
891     }
892     else if (type.items.size() == 1)
893     {
894       JSONSchemaTypeDefinition itemType = type.items.at(0);
895
896       // Loop through all array elements
897       for (unsigned int arrayIndex = 0; arrayIndex < value.size(); arrayIndex++)
898       {
899         CVariant temp;
900         JSON_STATUS status = checkType(value[arrayIndex], itemType, temp, errorData["property"]);
901         outputValue.push_back(temp);
902         if (status != OK)
903         {
904           CLog::Log(LOGWARNING, "JSONRPC: Array element at index %u does not match in type %s", arrayIndex, type.name.c_str());
905           errorMessage.Format("array element at index %u does not match", arrayIndex);
906           errorData["message"] = errorMessage.c_str();
907           return status;
908         }
909       }
910     }
911     // We have more than one element in "items"
912     // so we have tuple typing, which means that
913     // every element in the value array must match
914     // with the type at the same position in the
915     // "items" array
916     else
917     {
918       // If the number of elements in the value array
919       // does not match the number of elements in the
920       // "items" array and additional items are not
921       // allowed there is no need to check every element
922       if (value.size() < type.items.size() || (value.size() != type.items.size() && type.additionalItems.size() == 0))
923       {
924         CLog::Log(LOGWARNING, "JSONRPC: One of the array elements does not match in type %s", type.name.c_str());
925         errorMessage.Format("%d array elements expected but %d received", type.items.size(), value.size());
926         errorData["message"] = errorMessage.c_str();
927         return InvalidParams;
928       }
929
930       // Loop through all array elements until there
931       // are either no more schemas in the "items"
932       // array or no more elements in the value's array
933       unsigned int arrayIndex;
934       for (arrayIndex = 0; arrayIndex < min(type.items.size(), (size_t)value.size()); arrayIndex++)
935       {
936         JSON_STATUS status = checkType(value[arrayIndex], type.items.at(arrayIndex), outputValue[arrayIndex], errorData["property"]);
937         if (status != OK)
938         {
939           CLog::Log(LOGWARNING, "JSONRPC: Array element at index %u does not match with items schema in type %s", arrayIndex, type.name.c_str());
940           return status;
941         }
942       }
943
944       if (type.additionalItems.size() > 0)
945       {
946         // Loop through the rest of the elements
947         // in the array and check them against the
948         // "additionalItems"
949         for (; arrayIndex < value.size(); arrayIndex++)
950         {
951           bool ok = false;
952           for (unsigned int additionalIndex = 0; additionalIndex < type.additionalItems.size(); additionalIndex++)
953           {
954             CVariant dummyError;
955             if (checkType(value[arrayIndex], type.additionalItems.at(additionalIndex), outputValue[arrayIndex], dummyError) == OK)
956             {
957               ok = true;
958               break;
959             }
960           }
961
962           if (!ok)
963           {
964             CLog::Log(LOGWARNING, "JSONRPC: Array contains non-conforming additional items in type %s", type.name.c_str());
965             errorMessage.Format("Array element at index %u does not match the \"additionalItems\" schema", arrayIndex);
966             errorData["message"] = errorMessage.c_str();
967             return InvalidParams;
968           }
969         }
970       }
971     }
972
973     // If every array element is unique we need to check each one
974     if (type.uniqueItems)
975     {
976       for (unsigned int checkingIndex = 0; checkingIndex < outputValue.size(); checkingIndex++)
977       {
978         for (unsigned int checkedIndex = checkingIndex + 1; checkedIndex < outputValue.size(); checkedIndex++)
979         {
980           // If two elements are the same they are not unique
981           if (outputValue[checkingIndex] == outputValue[checkedIndex])
982           {
983             CLog::Log(LOGWARNING, "JSONRPC: Not unique array element at index %u and %u in type %s", checkingIndex, checkedIndex, type.name.c_str());
984             errorMessage.Format("Array element at index %u is not unique (same as array element at index %u)", checkingIndex, checkedIndex);
985             errorData["message"] = errorMessage.c_str();
986             return InvalidParams;
987           }
988         }
989       }
990     }
991
992     return OK;
993   }
994
995   // If it is an object we need to check every element
996   // against the defined "properties"
997   if (HasType(type.type, ObjectValue) && value.isObject())
998   {
999     unsigned int handled = 0;
1000     JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesEnd = type.properties.end();
1001     JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator propertiesIterator;
1002     for (propertiesIterator = type.properties.begin(); propertiesIterator != propertiesEnd; propertiesIterator++)
1003     {
1004       if (value.isMember(propertiesIterator->second.name))
1005       {
1006         JSON_STATUS status = checkType(value[propertiesIterator->second.name], propertiesIterator->second, outputValue[propertiesIterator->second.name], errorData["property"]);
1007         if (status != OK)
1008         {
1009           CLog::Log(LOGWARNING, "JSONRPC: Invalid property \"%s\" in type %s", propertiesIterator->second.name.c_str(), type.name.c_str());
1010           return status;
1011         }
1012         handled++;
1013       }
1014       else if (propertiesIterator->second.optional)
1015         outputValue[propertiesIterator->second.name] = propertiesIterator->second.defaultValue;
1016       else
1017       {
1018         CLog::Log(LOGWARNING, "JSONRPC: Missing property \"%s\" in type %s", propertiesIterator->second.name.c_str(), type.name.c_str());
1019         errorData["property"]["name"] = propertiesIterator->second.name.c_str();
1020         errorData["property"]["type"] = SchemaValueTypeToString(propertiesIterator->second.type);
1021         errorData["message"] = "Missing property";
1022         return InvalidParams;
1023       }
1024     }
1025
1026     // Additional properties are not allowed
1027     if (handled < value.size())
1028     {
1029       // If additional properties are allowed we need to check if
1030       // they match the defined schema
1031       if (type.hasAdditionalProperties && type.additionalProperties != NULL)
1032       {
1033         CVariant::const_iterator_map iter;
1034         CVariant::const_iterator_map iterEnd = value.end_map();
1035         for (iter = value.begin_map(); iter != iterEnd; iter++)
1036         {
1037           if (type.properties.find(iter->first) != type.properties.end())
1038             continue;
1039
1040           // If the additional property is of type "any"
1041           // we can simply copy its value to the output
1042           // object
1043           if (type.additionalProperties->type == AnyValue)
1044           {
1045             outputValue[iter->first] = value[iter->first];
1046             continue;
1047           }
1048
1049           JSON_STATUS status = checkType(value[iter->first], *(type.additionalProperties), outputValue[iter->first], errorData["property"]);
1050           if (status != OK)
1051           {
1052             CLog::Log(LOGWARNING, "JSONRPC: Invalid additional property \"%s\" in type %s", iter->first.c_str(), type.name.c_str());
1053             return status;
1054           }
1055         }
1056       }
1057       // If we still have unchecked properties but additional
1058       // properties are not allowed, we have invalid parameters
1059       else if (!type.hasAdditionalProperties || type.additionalProperties == NULL)
1060       {
1061         errorData["message"] = "Unexpected additional properties received";
1062         errorData.erase("property");
1063         return InvalidParams;
1064       }
1065     }
1066
1067     return OK;
1068   }
1069
1070   // It's neither an array nor an object
1071
1072   // If it can only take certain values ("enum")
1073   // we need to check against those
1074   if (type.enums.size() > 0)
1075   {
1076     bool valid = false;
1077     for (std::vector<CVariant>::const_iterator enumItr = type.enums.begin(); enumItr != type.enums.end(); enumItr++)
1078     {
1079       if (*enumItr == value)
1080       {
1081         valid = true;
1082         break;
1083       }
1084     }
1085
1086     if (!valid)
1087     {
1088       CLog::Log(LOGWARNING, "JSONRPC: Value does not match any of the enum values in type %s", type.name.c_str());
1089       errorData["message"] = "Received value does not match any of the defined enum values";
1090       return InvalidParams;
1091     }
1092   }
1093
1094   // If we have a number or an integer type, we need
1095   // to check the minimum and maximum values
1096   if ((HasType(type.type, NumberValue) && value.isDouble()) || (HasType(type.type, IntegerValue) && value.isInteger()))
1097   {
1098     double numberValue;
1099     if (value.isDouble())
1100       numberValue = value.asDouble();
1101     else
1102       numberValue = (double)value.asInteger();
1103     // Check minimum
1104     if ((type.exclusiveMinimum && numberValue <= type.minimum) || (!type.exclusiveMinimum && numberValue < type.minimum) ||
1105     // Check maximum
1106         (type.exclusiveMaximum && numberValue >= type.maximum) || (!type.exclusiveMaximum && numberValue > type.maximum))        
1107     {
1108       CLog::Log(LOGWARNING, "JSONRPC: Value does not lay between minimum and maximum in type %s", type.name.c_str());
1109       if (value.isDouble())
1110         errorMessage.Format("Value between %f (%s) and %f (%s) expected but %f received", 
1111           type.minimum, type.exclusiveMinimum ? "exclusive" : "inclusive", type.maximum, type.exclusiveMaximum ? "exclusive" : "inclusive", numberValue);
1112       else
1113         errorMessage.Format("Value between %d (%s) and %d (%s) expected but %d received", 
1114           (int)type.minimum, type.exclusiveMinimum ? "exclusive" : "inclusive", (int)type.maximum, type.exclusiveMaximum ? "exclusive" : "inclusive", (int)numberValue);
1115       errorData["message"] = errorMessage.c_str();
1116       return InvalidParams;
1117     }
1118     // Check divisibleBy
1119     if ((HasType(type.type, IntegerValue) && type.divisibleBy > 0 && ((int)numberValue % type.divisibleBy) != 0))
1120     {
1121       CLog::Log(LOGWARNING, "JSONRPC: Value does not meet divisibleBy requirements in type %s", type.name.c_str());
1122       errorMessage.Format("Value should be divisible by %d but %d received", type.divisibleBy, (int)numberValue);
1123       errorData["message"] = errorMessage.c_str();
1124       return InvalidParams;
1125     }
1126   }
1127
1128   // If we have a string, we need to check the length
1129   if (HasType(type.type, StringValue) && value.isString())
1130   {
1131     int size = strlen(value.asString());
1132     if (size < type.minLength)
1133     {
1134       CLog::Log(LOGWARNING, "JSONRPC: Value does not meet minLength requirements in type %s", type.name.c_str());
1135       errorMessage.Format("Value should have a minimum length of %d but has a length of %d", type.minLength, size);
1136       errorData["message"] = errorMessage.c_str();
1137       return InvalidParams;
1138     }
1139
1140     if (type.maxLength >= 0 && size > type.maxLength)
1141     {
1142       CLog::Log(LOGWARNING, "JSONRPC: Value does not meet maxLength requirements in type %s", type.name.c_str());
1143       errorMessage.Format("Value should have a maximum length of %d but has a length of %d", type.maxLength, size);
1144       errorData["message"] = errorMessage.c_str();
1145       return InvalidParams;
1146     }
1147   }
1148
1149   // Otherwise it can have any value
1150   outputValue = value;
1151   return OK;
1152 }
1153
1154 bool CJSONServiceDescription::parseMethod(const CVariant &value, JsonRpcMethod &method)
1155 {
1156   // Parse XBMC specific information about the method
1157   if (value.isMember("transport") && value["transport"].isArray())
1158   {
1159     int transport = 0;
1160     for (unsigned int index = 0; index < value["transport"].size(); index++)
1161       transport |= StringToTransportLayer(value["transport"][index].asString());
1162
1163     method.transportneed = (TransportLayerCapability)transport;
1164   }
1165   else
1166     method.transportneed = StringToTransportLayer(value.isMember("transport") ? value["transport"].asString() : "");
1167
1168   if (value.isMember("permission") && value["permission"].isArray())
1169   {
1170     int permissions = 0;
1171     for (unsigned int index = 0; index < value["permission"].size(); index++)
1172       permissions |= StringToPermission(value["permission"][index].asString());
1173
1174     method.permission = (OperationPermission)permissions;
1175   }
1176   else
1177     method.permission = StringToPermission(value.isMember("permission") ? value["permission"].asString() : "");
1178
1179   method.description = GetString(value["description"], "");
1180
1181   // Check whether there are parameters defined
1182   if (value.isMember("params") && value["params"].isArray())
1183   {
1184     // Loop through all defined parameters
1185     for (unsigned int paramIndex = 0; paramIndex < value["params"].size(); paramIndex++)
1186     {
1187       CVariant parameter = value["params"][paramIndex];
1188       // If the parameter definition does not contain a valid "name" or
1189       // "type" element we will ignore it
1190       if (!parameter.isMember("name") || !parameter["name"].isString() ||
1191          (!parameter.isMember("type") && !parameter.isMember("$ref") && !parameter.isMember("extends")) ||
1192          (parameter.isMember("type") && !parameter["type"].isString() && !parameter["type"].isArray()) || 
1193          (parameter.isMember("$ref") && !parameter["$ref"].isString()) ||
1194          (parameter.isMember("extends") && !parameter["extends"].isString() && !parameter["extends"].isArray()))
1195       {
1196         CLog::Log(LOGWARNING, "JSONRPC: Method %s has a badly defined parameter", method.name.c_str());
1197         return false;
1198       }
1199
1200       // Parse the parameter and add it to the list
1201       // of defined parameters
1202       JSONSchemaTypeDefinition param;
1203       if (!parseParameter(parameter, param))
1204         return false;
1205       method.parameters.push_back(param);
1206     }
1207   }
1208     
1209   // Parse the return value of the method
1210   parseReturn(value, method.returns);
1211
1212   return true;
1213 }
1214
1215 bool CJSONServiceDescription::parseParameter(CVariant &value, JSONSchemaTypeDefinition &parameter)
1216 {
1217   parameter.name = GetString(value["name"], "");
1218
1219   // Parse the type and default value of the parameter
1220   return parseTypeDefinition(value, parameter, true);
1221 }
1222
1223 bool CJSONServiceDescription::parseTypeDefinition(const CVariant &value, JSONSchemaTypeDefinition &type, bool isParameter)
1224 {
1225   bool isReferenceType = false;
1226   bool hasReference = false;
1227
1228   // Check if the type of the parameter defines a json reference
1229   // to a type defined somewhere else
1230   if (value.isMember("$ref") && value["$ref"].isString())
1231   {
1232     // Get the name of the referenced type
1233     std::string refType = value["$ref"].asString();
1234     // Check if the referenced type exists
1235     std::map<std::string, JSONSchemaTypeDefinition>::const_iterator iter = m_types.find(refType);
1236     if (refType.length() <= 0 || iter == m_types.end())
1237     {
1238       CLog::Log(LOGWARNING, "JSONRPC: JSON schema type %s references an unknown type %s", type.name.c_str(), refType.c_str());
1239       return false;
1240     }
1241     
1242     std::string typeName = type.name;
1243     type = iter->second;
1244     if (!typeName.empty())
1245       type.name = typeName;
1246     hasReference = true;
1247   }
1248   else if (value.isMember("id") && value["id"].isString())
1249   {
1250     type.ID = GetString(value["id"], "");
1251     isReferenceType = true;
1252   }
1253
1254   // Check if the "required" field has been defined
1255   type.optional = value.isMember("required") && value["required"].isBoolean() ? !value["required"].asBoolean() : true;
1256
1257   // Get the "description"
1258   if (!hasReference || (value.isMember("description") && value["description"].isString()))
1259     type.description = GetString(value["description"], "");
1260
1261   if (hasReference)
1262   {
1263     // If there is a specific default value, read it
1264     if (value.isMember("default") && IsType(value["default"], type.type))
1265     {
1266       bool ok = false;
1267       if (type.enums.size() >= 0)
1268         ok = true;
1269       // If the type has an enum definition we must make
1270       // sure that the default value is a valid enum value
1271       else
1272       {
1273         for (std::vector<CVariant>::const_iterator itr = type.enums.begin(); itr != type.enums.end(); itr++)
1274         {
1275           if (value["default"] == *itr)
1276           {
1277             ok = true;
1278             break;
1279           }
1280         }
1281       }
1282
1283       if (ok)
1284         type.defaultValue = value["default"];
1285     }
1286
1287     return true;
1288   }
1289
1290   // Check whether this type extends an existing type
1291   if (value.isMember("extends"))
1292   {
1293     if (value["extends"].isString())
1294     {
1295       std::string extends = GetString(value["extends"], "");
1296       if (!extends.empty())
1297       {
1298         std::map<std::string, JSONSchemaTypeDefinition>::const_iterator iter = m_types.find(extends);
1299         if (iter == m_types.end())
1300         {
1301           CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", type.name.c_str(), extends.c_str());
1302           return false;
1303         }
1304
1305         type.type = iter->second.type;
1306         type.extends.push_back(iter->second);
1307       }
1308     }
1309     else if (value["extends"].isArray())
1310     {
1311       std::string extends;
1312       JSONSchemaType extendedType;
1313       for (unsigned int extendsIndex = 0; extendsIndex < value["extends"].size(); extendsIndex++)
1314       {
1315         extends = GetString(value["extends"][extendsIndex], "");
1316         if (!extends.empty())
1317         {
1318           std::map<std::string, JSONSchemaTypeDefinition>::const_iterator iter = m_types.find(extends);
1319           if (iter == m_types.end())
1320           {
1321             type.extends.clear();
1322             CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends an unknown type %s", type.name.c_str(), extends.c_str());
1323             return false;
1324           }
1325
1326           if (extendsIndex == 0)
1327             extendedType = iter->second.type;
1328           else if (extendedType != iter->second.type)
1329           {
1330             type.extends.clear();
1331             CLog::Log(LOGDEBUG, "JSONRPC: JSON schema type %s extends multiple JSON schema types of mismatching types", type.name.c_str());
1332             return false;
1333           }
1334
1335           type.extends.push_back(iter->second);
1336         }
1337       }
1338
1339       type.type = extendedType;
1340     }
1341   }
1342
1343   // Only read the "type" attribute if it's
1344   // not an extending type
1345   if (type.extends.size() <= 0)
1346   {
1347     // Get the defined type of the parameter
1348     type.type = parseJSONSchemaType(value["type"]);
1349   }
1350
1351   if (HasType(type.type, ObjectValue))
1352   {
1353     // If the type definition is of type "object"
1354     // and has a "properties" definition we need
1355     // to handle these as well
1356     if (value.isMember("properties") && value["properties"].isObject())
1357     {
1358       // Get all child elements of the "properties"
1359       // object and loop through them
1360       for (CVariant::const_iterator_map itr = value["properties"].begin_map(); itr != value["properties"].end_map(); itr++)
1361       {
1362         // Create a new type definition, store the name
1363         // of the current property into it, parse it
1364         // recursively and add its default value
1365         // to the current type's default value
1366         JSONSchemaTypeDefinition propertyType;
1367         propertyType.name = itr->first;
1368         if (!parseTypeDefinition(itr->second, propertyType, false))
1369           return false;
1370         type.defaultValue[itr->first] = propertyType.defaultValue;
1371         type.properties.add(propertyType);
1372       }
1373     }
1374
1375     type.hasAdditionalProperties = true;
1376     type.additionalProperties = new JSONSchemaTypeDefinition();
1377     if (value.isMember("additionalProperties"))
1378     {
1379       if (value["additionalProperties"].isBoolean())
1380       {
1381         type.hasAdditionalProperties = value["additionalProperties"].asBoolean();
1382         if (!type.hasAdditionalProperties)
1383         {
1384           delete type.additionalProperties;
1385           type.additionalProperties = NULL;
1386         }
1387       }
1388       else if (value["additionalProperties"].isObject() && !value["additionalProperties"].isNull())
1389       {
1390         if (!parseTypeDefinition(value["additionalProperties"], *(type.additionalProperties), false))
1391         {
1392           type.hasAdditionalProperties = false;
1393           delete type.additionalProperties;
1394           type.additionalProperties = NULL;
1395           CLog::Log(LOGWARNING, "JSONRPC: Invalid additionalProperties schema definition in type %s", type.name.c_str());
1396         }
1397       }
1398       else
1399         CLog::Log(LOGWARNING, "JSONRPC: Invalid additionalProperties definition in type %s", type.name.c_str());
1400     }
1401   }
1402
1403   // If the defined parameter is an array
1404   // we need to check for detailed definitions
1405   // of the array items
1406   if (HasType(type.type, ArrayValue))
1407   {
1408     // Check for "uniqueItems" field
1409     if (value.isMember("uniqueItems") && value["uniqueItems"].isBoolean())
1410       type.uniqueItems = value["uniqueItems"].asBoolean();
1411     else
1412       type.uniqueItems = false;
1413
1414     // Check for "additionalItems" field
1415     if (value.isMember("additionalItems"))
1416     {
1417       // If it is an object, there is only one schema for it
1418       if (value["additionalItems"].isObject())
1419       {
1420         JSONSchemaTypeDefinition additionalItem;
1421
1422         if (parseTypeDefinition(value["additionalItems"], additionalItem, false))
1423           type.additionalItems.push_back(additionalItem);
1424       }
1425       // If it is an array there may be multiple schema definitions
1426       else if (value["additionalItems"].isArray())
1427       {
1428         for (unsigned int itemIndex = 0; itemIndex < value["additionalItems"].size(); itemIndex++)
1429         {
1430           JSONSchemaTypeDefinition additionalItem;
1431
1432           if (parseTypeDefinition(value["additionalItems"][itemIndex], additionalItem, false))
1433             type.additionalItems.push_back(additionalItem);
1434         }
1435       }
1436       // If it is not a (array of) schema and not a bool (default value is false)
1437       // it has an invalid value
1438       else if (!value["additionalItems"].isBoolean())
1439         CLog::Log(LOGWARNING, "Invalid \"additionalItems\" value for type %s", type.name.c_str());
1440     }
1441
1442     // If the "items" field is a single object
1443     // we can parse that directly
1444     if (value.isMember("items"))
1445     {
1446       if (value["items"].isObject())
1447       {
1448         JSONSchemaTypeDefinition item;
1449
1450         if (!parseTypeDefinition(value["items"], item, false))
1451           return false;
1452         type.items.push_back(item);
1453       }
1454       // Otherwise if it is an array we need to
1455       // parse all elements and store them
1456       else if (value["items"].isArray())
1457       {
1458         for (CVariant::const_iterator_array itemItr = value["items"].begin_array(); itemItr != value["items"].end_array(); itemItr++)
1459         {
1460           JSONSchemaTypeDefinition item;
1461
1462           if (!parseTypeDefinition(*itemItr, item, false))
1463             return false;
1464           type.items.push_back(item);
1465         }
1466       }
1467     }
1468
1469     type.minItems = (unsigned int)value["minItems"].asUnsignedInteger(0);
1470     type.maxItems = (unsigned int)value["maxItems"].asUnsignedInteger(0);
1471   }
1472
1473   if (HasType(type.type, NumberValue) || HasType(type.type, IntegerValue))
1474   {
1475     if ((type.type & NumberValue) == NumberValue)
1476     {
1477       type.minimum = value["minimum"].asDouble(-numeric_limits<double>::max());
1478       type.maximum = value["maximum"].asDouble(numeric_limits<double>::max());
1479     }
1480     else if ((type.type  & IntegerValue) == IntegerValue)
1481     {
1482       type.minimum = (double)value["minimum"].asInteger(numeric_limits<int>::min());
1483       type.maximum = (double)value["maximum"].asInteger(numeric_limits<int>::max());
1484     }
1485
1486     type.exclusiveMinimum = value["exclusiveMinimum"].asBoolean(false);
1487     type.exclusiveMaximum = value["exclusiveMaximum"].asBoolean(false);
1488     type.divisibleBy = (unsigned int)value["divisibleBy"].asUnsignedInteger(0);
1489   }
1490       
1491   if (HasType(type.type, StringValue))
1492   {
1493     type.minLength = (int)value["minLength"].asInteger(-1);
1494     type.maxLength = (int)value["maxLength"].asInteger(-1);
1495   }
1496
1497   // If the type definition is neither an
1498   // "object" nor an "array" we can check
1499   // for an "enum" definition
1500   if (value.isMember("enum") && value["enum"].isArray())
1501   {
1502     // Loop through all elements in the "enum" array
1503     for (CVariant::const_iterator_array enumItr = value["enum"].begin_array(); enumItr != value["enum"].end_array(); enumItr++)
1504     {
1505       // Check for duplicates and eliminate them
1506       bool approved = true;
1507       for (unsigned int approvedIndex = 0; approvedIndex < type.enums.size(); approvedIndex++)
1508       {
1509         if (*enumItr == type.enums.at(approvedIndex))
1510         {
1511           approved = false;
1512           break;
1513         }
1514       }
1515
1516       // Only add the current item to the enum value 
1517       // list if it is not duplicate
1518       if (approved)
1519         type.enums.push_back(*enumItr);
1520     }
1521   }
1522
1523   if (type.type != ObjectValue)
1524   {
1525     // If there is a definition for a default value and its type
1526     // matches the type of the parameter we can parse it
1527     bool ok = false;
1528     if (value.isMember("default") && IsType(value["default"], type.type))
1529     {
1530       if (type.enums.size() >= 0)
1531         ok = true;
1532       // If the type has an enum definition we must make
1533       // sure that the default value is a valid enum value
1534       else
1535       {
1536         for (std::vector<CVariant>::const_iterator itr = type.enums.begin(); itr != type.enums.end(); itr++)
1537         {
1538           if (value["default"] == *itr)
1539           {
1540             ok = true;
1541             break;
1542           }
1543         }
1544       }
1545     }
1546
1547     if (ok)
1548       type.defaultValue = value["default"];
1549     else
1550     {
1551       // If the type of the default value definition does not
1552       // match the type of the parameter we have to log this
1553       if (value.isMember("default") && !IsType(value["default"], type.type))
1554         CLog::Log(LOGWARNING, "JSONRPC: Parameter %s has an invalid default value", type.name.c_str());
1555       
1556       // If the type contains an "enum" we need to get the
1557       // default value from the first enum value
1558       if (type.enums.size() > 0)
1559         type.defaultValue = type.enums.at(0);
1560       // otherwise set a default value instead
1561       else
1562         SetDefaultValue(type.defaultValue, type.type);
1563     }
1564   }
1565
1566   if (isReferenceType)
1567     addReferenceTypeDefinition(type);
1568
1569   return true;
1570 }
1571
1572 void CJSONServiceDescription::parseReturn(const CVariant &value, JSONSchemaTypeDefinition &returns)
1573 {
1574   // Only parse the "returns" definition if there is one
1575   if (!value.isMember("returns"))
1576   {
1577     returns.type = NullValue;
1578     return;
1579   }
1580   
1581   // If the type of the return value is defined as a simple string we can parse it directly
1582   if (value["returns"].isString())
1583   {
1584     returns.type = parseJSONSchemaType(value["returns"]);
1585   }
1586   // otherwise we have to parse the whole type definition
1587   else
1588     parseTypeDefinition(value["returns"], returns, false);
1589 }
1590
1591 JSONSchemaType CJSONServiceDescription::parseJSONSchemaType(const CVariant &value)
1592 {
1593   if (value.isArray())
1594   {
1595     int parsedType = 0;
1596     // If the defined type is an array, we have
1597     // to handle a union type
1598     for (unsigned int typeIndex = 0; typeIndex < value.size(); typeIndex++)
1599     {
1600       // If the type is a string try to parse it
1601       if (value[typeIndex].isString())
1602         parsedType |= StringToSchemaValueType(value[typeIndex].asString());
1603       else
1604         CLog::Log(LOGWARNING, "JSONRPC: Invalid type in union type definition");
1605     }
1606
1607     // If the type has not been set yet set it to "any"
1608     if (parsedType == 0)
1609       return AnyValue;
1610
1611     return (JSONSchemaType)parsedType;
1612   }
1613   else
1614     return value.isString() ? StringToSchemaValueType(value.asString()) : AnyValue;
1615 }
1616
1617 void CJSONServiceDescription::addReferenceTypeDefinition(JSONSchemaTypeDefinition &typeDefinition)
1618 {
1619   // If the given json value is no object or does not contain an "id" field
1620   // of type string it is no valid type definition
1621   if (typeDefinition.ID.empty())
1622     return;
1623
1624   // If the id has already been defined we ignore the type definition
1625   if (m_types.find(typeDefinition.ID) != m_types.end())
1626     return;
1627
1628   // Add the type to the list of type definitions
1629   m_types[typeDefinition.ID] = typeDefinition;
1630 }
1631
1632 void CJSONServiceDescription::getReferencedTypes(const JSONSchemaTypeDefinition &type, std::vector<std::string> &referencedTypes)
1633 {
1634   // If the current type is a referenceable object, we can add it to the list
1635   if (type.ID.size() > 0)
1636   {
1637     for (unsigned int index = 0; index < referencedTypes.size(); index++)
1638     {
1639       // The referenceable object has already been added to the list so we can just skip it
1640       if (type.ID == referencedTypes.at(index))
1641         return;
1642     }
1643
1644     referencedTypes.push_back(type.ID);
1645   }
1646
1647   // If the current type is an object we need to check its properties
1648   if (HasType(type.type, ObjectValue))
1649   {
1650     JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iter;
1651     JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iterEnd = type.properties.end();
1652     for (iter = type.properties.begin(); iter != iterEnd; iter++)
1653       getReferencedTypes(iter->second, referencedTypes);
1654   }
1655   // If the current type is an array we need to check its items
1656   if (HasType(type.type, ArrayValue))
1657   {
1658     unsigned int index;
1659     for (index = 0; index < type.items.size(); index++)
1660       getReferencedTypes(type.items.at(index), referencedTypes);
1661
1662     for (index = 0; index < type.additionalItems.size(); index++)
1663       getReferencedTypes(type.additionalItems.at(index), referencedTypes);
1664   }
1665
1666   // If the current type extends others type we need to check those types
1667   for (unsigned int index = 0; index < type.extends.size(); index++)
1668     getReferencedTypes(type.extends.at(index), referencedTypes);
1669 }
1670
1671 CJSONServiceDescription::CJsonRpcMethodMap::CJsonRpcMethodMap()
1672 {
1673   m_actionmap = std::map<std::string, JsonRpcMethod>();
1674 }
1675
1676 void CJSONServiceDescription::CJsonRpcMethodMap::add(const JsonRpcMethod &method)
1677 {
1678   CStdString name = method.name;
1679   name = name.ToLower();
1680   m_actionmap[name] = method;
1681 }
1682
1683 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::begin() const
1684 {
1685   return m_actionmap.begin();
1686 }
1687
1688 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::find(const std::string& key) const
1689 {
1690   return m_actionmap.find(key);
1691 }
1692
1693 CJSONServiceDescription::CJsonRpcMethodMap::JsonRpcMethodIterator CJSONServiceDescription::CJsonRpcMethodMap::end() const
1694 {
1695   return m_actionmap.end();
1696 }