[windows] Fix MAC Address Discovery
[xbmc.git] / xbmc / filesystem / UPnPDirectory.cpp
blob2b2af57598a1733ff8f960e3830a8e506271f066
1 /*
2 * UPnP Support for XBMC
3 * Copyright (c) 2006 c0diq (Sylvain Rebaud)
4 * Portions Copyright (c) by the authors of libPlatinum
5 * http://www.plutinosoft.com/blog/category/platinum/
7 * Copyright (C) 2010-2018 Team Kodi
8 * This file is part of Kodi - https://kodi.tv
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 * See LICENSES/README.md for more information.
13 #include "UPnPDirectory.h"
15 #include "FileItem.h"
16 #include "FileItemList.h"
17 #include "ServiceBroker.h"
18 #include "URL.h"
19 #include "network/upnp/UPnP.h"
20 #include "network/upnp/UPnPInternal.h"
21 #include "settings/Settings.h"
22 #include "settings/SettingsComponent.h"
23 #include "utils/StringUtils.h"
24 #include "utils/URIUtils.h"
25 #include "utils/log.h"
27 #include <Platinum/Source/Devices/MediaServer/PltSyncMediaBrowser.h>
28 #include <Platinum/Source/Platinum/Platinum.h>
30 using namespace MUSIC_INFO;
31 using namespace XFILE;
32 using namespace UPNP;
34 namespace XFILE
37 static std::string GetContentMapping(NPT_String& objectClass)
39 struct SClassMapping
41 const char* ObjectClass;
42 const char* Content;
44 static const SClassMapping mapping[] = {
45 { "object.item.videoItem.videoBroadcast" , "episodes" }
46 , { "object.item.videoItem.musicVideoClip" , "musicvideos" }
47 , { "object.item.videoItem" , "movies" }
48 , { "object.item.audioItem.musicTrack" , "songs" }
49 , { "object.item.audioItem" , "songs" }
50 , { "object.item.imageItem.photo" , "photos" }
51 , { "object.item.imageItem" , "photos" }
52 , { "object.container.album.videoAlbum.videoBroadcastShow" , "tvshows" }
53 , { "object.container.album.videoAlbum.videoBroadcastSeason", "seasons" }
54 , { "object.container.album.musicAlbum" , "albums" }
55 , { "object.container.album.photoAlbum" , "photos" }
56 , { "object.container.album" , "albums" }
57 , { "object.container.person" , "artists" }
58 , { NULL , NULL }
60 for(const SClassMapping* map = mapping; map->ObjectClass; map++)
62 if(objectClass.StartsWith(map->ObjectClass, true))
64 return map->Content;
65 break;
68 return "unknown";
71 static bool FindDeviceWait(CUPnP* upnp, const char* uuid, PLT_DeviceDataReference& device)
73 bool client_started = upnp->IsClientStarted();
74 upnp->StartClient();
76 // look for device in our list
77 // (and wait for it to respond for 5 secs if we're just starting upnp client)
78 NPT_TimeStamp watchdog;
79 NPT_System::GetCurrentTimeStamp(watchdog);
80 watchdog += 5.0;
82 for (;;) {
83 if (NPT_SUCCEEDED(upnp->m_MediaBrowser->FindServer(uuid, device)) && !device.IsNull())
84 break;
86 // fail right away if device not found and upnp client was already running
87 if (client_started)
88 return false;
90 // otherwise check if we've waited long enough without success
91 NPT_TimeStamp now;
92 NPT_System::GetCurrentTimeStamp(now);
93 if (now > watchdog)
94 return false;
96 // sleep a bit and try again
97 NPT_System::Sleep(NPT_TimeInterval((double)1));
100 return !device.IsNull();
103 /*----------------------------------------------------------------------
104 | CUPnPDirectory::GetFriendlyName
105 +---------------------------------------------------------------------*/
106 std::string CUPnPDirectory::GetFriendlyName(const CURL& url)
108 NPT_String path = url.Get().c_str();
109 if (!path.EndsWith("/")) path += "/";
111 if (path.Left(7).Compare("upnp://", true) != 0) {
112 return {};
113 } else if (path.Compare("upnp://", true) == 0) {
114 return "UPnP Media Servers (Auto-Discover)";
117 // look for nextslash
118 int next_slash = path.Find('/', 7);
119 if (next_slash == -1)
120 return {};
122 NPT_String uuid = path.SubString(7, next_slash-7);
123 NPT_String object_id = path.SubString(next_slash+1, path.GetLength()-next_slash-2);
125 // look for device
126 PLT_DeviceDataReference device;
127 if(!FindDeviceWait(CUPnP::GetInstance(), uuid, device))
128 return {};
130 return device->GetFriendlyName().GetChars();
133 /*----------------------------------------------------------------------
134 | CUPnPDirectory::GetDirectory
135 +---------------------------------------------------------------------*/
136 bool CUPnPDirectory::GetResource(const CURL& path, CFileItem &item)
138 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP))
139 return false;
141 if(!path.IsProtocol("upnp"))
142 return false;
144 CUPnP* upnp = CUPnP::GetInstance();
145 if(!upnp)
146 return false;
148 const std::string& uuid = path.GetHostName();
149 std::string object = path.GetFileName();
150 StringUtils::TrimRight(object, "/");
151 object = CURL::Decode(object);
153 PLT_DeviceDataReference device;
154 if(!FindDeviceWait(upnp, uuid.c_str(), device)) {
155 CLog::Log(LOGERROR, "CUPnPDirectory::GetResource - unable to find uuid {}", uuid);
156 return false;
159 PLT_MediaObjectListReference list;
160 if (NPT_FAILED(upnp->m_MediaBrowser->BrowseSync(device, object.c_str(), list, true))) {
161 CLog::Log(LOGERROR, "CUPnPDirectory::GetResource - unable to find object {}", object);
162 return false;
165 if (list.IsNull() || !list->GetItemCount()) {
166 CLog::Log(LOGERROR, "CUPnPDirectory::GetResource - no items returned for object {}", object);
167 return false;
170 PLT_MediaObjectList::Iterator entry = list->GetFirstItem();
171 if (entry == 0)
172 return false;
174 return UPNP::GetResource(*entry, item);
178 /*----------------------------------------------------------------------
179 | CUPnPDirectory::GetDirectory
180 +---------------------------------------------------------------------*/
181 bool
182 CUPnPDirectory::GetDirectory(const CURL& url, CFileItemList &items)
184 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP))
185 return false;
187 CUPnP* upnp = CUPnP::GetInstance();
189 /* upnp should never be cached, it has internal cache */
190 items.SetCacheToDisc(CFileItemList::CACHE_NEVER);
192 // We accept upnp://devuuid/[item_id/]
193 NPT_String path = url.Get().c_str();
194 if (!path.StartsWith("upnp://", true)) {
195 return false;
198 if (path.Compare("upnp://", true) == 0) {
199 upnp->StartClient();
201 // root -> get list of devices
202 const NPT_Lock<PLT_DeviceDataReferenceList>& devices = upnp->m_MediaBrowser->GetMediaServers();
203 NPT_List<PLT_DeviceDataReference>::Iterator device = devices.GetFirstItem();
204 while (device) {
205 NPT_String name = (*device)->GetFriendlyName();
206 NPT_String uuid = (*device)->GetUUID();
208 CFileItemPtr pItem(new CFileItem((const char*)name));
209 pItem->SetPath(std::string((const char*) "upnp://" + uuid + "/"));
210 pItem->m_bIsFolder = true;
211 pItem->SetArt("thumb", (const char*)(*device)->GetIconUrl("image/png"));
213 items.Add(pItem);
215 ++device;
217 } else {
218 if (!path.EndsWith("/")) path += "/";
220 // look for nextslash
221 int next_slash = path.Find('/', 7);
223 NPT_String uuid = (next_slash==-1)?path.SubString(7):path.SubString(7, next_slash-7);
224 NPT_String object_id = (next_slash == -1) ? NPT_String("") : path.SubString(next_slash + 1);
225 object_id.TrimRight("/");
226 if (object_id.GetLength()) {
227 object_id = CURL::Decode((char*)object_id).c_str();
230 // try to find the device with wait on startup
231 PLT_DeviceDataReference device;
232 if (!FindDeviceWait(upnp, uuid, device))
233 goto failure;
235 // issue a browse request with object_id
236 // if object_id is empty use "0" for root
237 object_id = object_id.IsEmpty() ? NPT_String("0") : object_id;
239 // remember a count of object classes
240 std::map<NPT_String, int> classes;
242 // just a guess as to what types of files we want
243 bool video = true;
244 bool audio = true;
245 bool image = true;
246 StringUtils::TrimLeft(m_strFileMask, "/");
247 if (!m_strFileMask.empty()) {
248 video = m_strFileMask.find(".wmv") != std::string::npos;
249 audio = m_strFileMask.find(".wma") != std::string::npos;
250 image = m_strFileMask.find(".jpg") != std::string::npos;
253 // special case for Windows Media Connect and WMP11 when looking for root
254 // We can target which root subfolder we want based on directory mask
255 if (object_id == "0" && ((device->GetFriendlyName().Find("Windows Media Connect", 0, true) >= 0) ||
256 (device->m_ModelName == "Windows Media Player Sharing"))) {
258 // look for a specific type to differentiate which folder we want
259 if (audio && !video && !image) {
260 // music
261 object_id = "1";
262 } else if (!audio && video && !image) {
263 // video
264 object_id = "2";
265 } else if (!audio && !video && image) {
266 // pictures
267 object_id = "3";
271 #ifdef DISABLE_SPECIALCASE
272 // same thing but special case for XBMC
273 if (object_id == "0" && ((device->m_ModelName.Find("XBMC", 0, true) >= 0) ||
274 (device->m_ModelName.Find("Xbox Media Center", 0, true) >= 0))) {
275 // look for a specific type to differentiate which folder we want
276 if (audio && !video && !image) {
277 // music
278 object_id = "virtualpath://upnpmusic";
279 } else if (!audio && video && !image) {
280 // video
281 object_id = "virtualpath://upnpvideo";
282 } else if (!audio && !video && image) {
283 // pictures
284 object_id = "virtualpath://upnppictures";
287 #endif
289 // if error, return now, the device could have gone away
290 // this will make us go back to the sources list
291 PLT_MediaObjectListReference list;
292 NPT_Result res = upnp->m_MediaBrowser->BrowseSync(device, object_id, list);
293 if (NPT_FAILED(res)) goto failure;
295 // empty list is ok
296 if (list.IsNull()) goto cleanup;
298 PLT_MediaObjectList::Iterator entry = list->GetFirstItem();
299 while (entry) {
300 // disregard items with wrong class/type
301 if( (!video && (*entry)->m_ObjectClass.type.CompareN("object.item.videoitem", 21,true) == 0)
302 || (!audio && (*entry)->m_ObjectClass.type.CompareN("object.item.audioitem", 21,true) == 0)
303 || (!image && (*entry)->m_ObjectClass.type.CompareN("object.item.imageitem", 21,true) == 0) )
305 ++entry;
306 continue;
309 // keep count of classes
310 classes[(*entry)->m_ObjectClass.type]++;
311 CFileItemPtr pItem = BuildObject(*entry, UPnPClient);
312 if(!pItem) {
313 ++entry;
314 continue;
317 std::string id;
318 if ((*entry)->m_ReferenceID.IsEmpty())
319 id = (const char*) (*entry)->m_ObjectID;
320 else
321 id = (const char*) (*entry)->m_ReferenceID;
323 id = CURL::Encode(id);
324 URIUtils::AddSlashAtEnd(id);
325 pItem->SetPath(std::string((const char*) "upnp://" + uuid + "/" + id.c_str()));
327 items.Add(pItem);
329 ++entry;
332 NPT_String max_string = "";
333 int max_count = 0;
334 for (auto& it : classes)
336 if (it.second > max_count)
338 max_string = it.first;
339 max_count = it.second;
342 std::string content = GetContentMapping(max_string);
343 items.SetContent(content);
344 if (content == "unknown")
346 items.AddSortMethod(SortByNone, 571, LABEL_MASKS("%L", "%I", "%L", ""));
347 items.AddSortMethod(SortByLabel, SortAttributeIgnoreFolders, 551, LABEL_MASKS("%L", "%I", "%L", ""));
348 items.AddSortMethod(SortBySize, 553, LABEL_MASKS("%L", "%I", "%L", "%I"));
349 items.AddSortMethod(SortByDate, 552, LABEL_MASKS("%L", "%J", "%L", "%J"));
353 cleanup:
354 return true;
356 failure:
357 return false;
361 bool CUPnPDirectory::Resolve(CFileItem& item) const
363 return GetResource(item.GetURL(), item);