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