1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "FileItem.h"
10 #include "MediaManager.h"
11 #include "ServiceBroker.h"
12 #include "guilib/GUIComponent.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "URL.h"
15 #include "utils/URIUtils.h"
16 #ifdef TARGET_WINDOWS
17 #include "platform/win32/WIN32Util.h"
18 #include "utils/CharsetConverter.h"
19 #endif
20 #include "guilib/GUIWindowManager.h"
21 #ifdef HAS_DVD_DRIVE
22 #ifndef TARGET_WINDOWS
23 //! @todo switch all ports to use auto sources
24 #include <map>
25 #include <utility>
26 #include "DetectDVDType.h"
27 #endif
28 #endif
29 #include "Autorun.h"
30 #include "AutorunMediaJob.h"
31 #include "GUIUserMessages.h"
32 #include "addons/VFSEntry.h"
33 #include "cores/VideoPlayer/DVDInputStreams/DVDInputStreamNavigator.h"
34 #include "dialogs/GUIDialogKaiToast.h"
35 #include "dialogs/GUIDialogPlayEject.h"
36 #include "filesystem/File.h"
37 #include "messaging/helpers/DialogOKHelper.h"
38 #include "settings/MediaSourceSettings.h"
39 #include "settings/Settings.h"
40 #include "settings/SettingsComponent.h"
41 #include "threads/SingleLock.h"
42 #include "utils/JobManager.h"
43 #include "utils/StringUtils.h"
44 #include "utils/XBMCTinyXML.h"
45 #include "utils/log.h"
46 
47 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !(defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
48 #include <sys/ioctl.h>
49 #include <linux/cdrom.h>
50 #endif
51 #include <string>
52 #include <vector>
53 
54 #ifdef HAVE_LIBBLURAY
55 #include "filesystem/BlurayDirectory.h"
56 #endif
57 
58 using namespace XFILE;
59 
60 #ifdef HAS_DVD_DRIVE
61 using namespace MEDIA_DETECT;
62 #endif
63 
64 const char MEDIA_SOURCES_XML[] = { "special://profile/mediasources.xml" };
65 
CMediaManager()66 CMediaManager::CMediaManager()
67 {
68   m_bhasoptical = false;
69   m_platformStorage = nullptr;
70 }
71 
Stop()72 void CMediaManager::Stop()
73 {
74   if (m_platformStorage)
75     m_platformStorage->Stop();
76 
77   delete m_platformStorage;
78   m_platformStorage = nullptr;
79 }
80 
Initialize()81 void CMediaManager::Initialize()
82 {
83   if (!m_platformStorage)
84   {
85     m_platformStorage = IStorageProvider::CreateInstance();
86   }
87 #ifdef HAS_DVD_DRIVE
88   m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
89 #endif
90   m_platformStorage->Initialize();
91 }
92 
LoadSources()93 bool CMediaManager::LoadSources()
94 {
95   // clear our location list
96   m_locations.clear();
97 
98   // load xml file...
99   CXBMCTinyXML xmlDoc;
100   if ( !xmlDoc.LoadFile( MEDIA_SOURCES_XML ) )
101     return false;
102 
103   TiXmlElement* pRootElement = xmlDoc.RootElement();
104   if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "mediasources") != 0)
105   {
106     CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", MEDIA_SOURCES_XML, xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
107     return false;
108   }
109 
110   // load the <network> block
111   TiXmlNode *pNetwork = pRootElement->FirstChild("network");
112   if (pNetwork)
113   {
114     TiXmlElement *pLocation = pNetwork->FirstChildElement("location");
115     while (pLocation)
116     {
117       CNetworkLocation location;
118       pLocation->Attribute("id", &location.id);
119       if (pLocation->FirstChild())
120       {
121         location.path = pLocation->FirstChild()->Value();
122         m_locations.push_back(location);
123       }
124       pLocation = pLocation->NextSiblingElement("location");
125     }
126   }
127   return true;
128 }
129 
SaveSources()130 bool CMediaManager::SaveSources()
131 {
132   CXBMCTinyXML xmlDoc;
133   TiXmlElement xmlRootElement("mediasources");
134   TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
135   if (!pRoot) return false;
136 
137   TiXmlElement networkNode("network");
138   TiXmlNode *pNetworkNode = pRoot->InsertEndChild(networkNode);
139   if (pNetworkNode)
140   {
141     for (std::vector<CNetworkLocation>::iterator it = m_locations.begin(); it != m_locations.end(); ++it)
142     {
143       TiXmlElement locationNode("location");
144       locationNode.SetAttribute("id", (*it).id);
145       TiXmlText value((*it).path);
146       locationNode.InsertEndChild(value);
147       pNetworkNode->InsertEndChild(locationNode);
148     }
149   }
150   return xmlDoc.SaveFile(MEDIA_SOURCES_XML);
151 }
152 
GetLocalDrives(VECSOURCES & localDrives,bool includeQ)153 void CMediaManager::GetLocalDrives(VECSOURCES &localDrives, bool includeQ)
154 {
155   CSingleLock lock(m_CritSecStorageProvider);
156   m_platformStorage->GetLocalDrives(localDrives);
157 }
158 
GetRemovableDrives(VECSOURCES & removableDrives)159 void CMediaManager::GetRemovableDrives(VECSOURCES &removableDrives)
160 {
161   CSingleLock lock(m_CritSecStorageProvider);
162   if (m_platformStorage)
163     m_platformStorage->GetRemovableDrives(removableDrives);
164 }
165 
GetNetworkLocations(VECSOURCES & locations,bool autolocations)166 void CMediaManager::GetNetworkLocations(VECSOURCES &locations, bool autolocations)
167 {
168   // Load our xml file
169   LoadSources();
170   for (unsigned int i = 0; i < m_locations.size(); i++)
171   {
172     CMediaSource share;
173     share.strPath = m_locations[i].path;
174     CURL url(share.strPath);
175     share.strName = url.GetWithoutUserDetails();
176     locations.push_back(share);
177   }
178   if (autolocations)
179   {
180     CMediaSource share;
181     share.m_ignore = true;
182 #ifdef HAS_FILESYSTEM_SMB
183     share.strPath = "smb://";
184     share.strName = g_localizeStrings.Get(20171);
185     locations.push_back(share);
186 #endif
187 
188 #ifdef HAS_FILESYSTEM_NFS
189     share.strPath = "nfs://";
190     share.strName = g_localizeStrings.Get(20259);
191     locations.push_back(share);
192 #endif// HAS_FILESYSTEM_NFS
193 
194 #ifdef HAS_UPNP
195     if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SERVICES_UPNP))
196     {
197       const std::string& strDevices = g_localizeStrings.Get(33040); //"% Devices"
198       share.strPath = "upnp://";
199       share.strName = StringUtils::Format(strDevices.c_str(), "UPnP"); //"UPnP Devices"
200       locations.push_back(share);
201     }
202 #endif
203 
204 #ifdef HAS_ZEROCONF
205     share.strPath = "zeroconf://";
206     share.strName = g_localizeStrings.Get(20262);
207     locations.push_back(share);
208 #endif
209 
210     if (CServiceBroker::IsBinaryAddonCacheUp())
211     {
212       for (const auto& addon : CServiceBroker::GetVFSAddonCache().GetAddonInstances())
213       {
214         const auto& info = addon->GetProtocolInfo();
215         if (!info.type.empty() && info.supportBrowsing)
216         {
217           share.strPath = info.type + "://";
218           share.strName = g_localizeStrings.GetAddonString(addon->ID(), info.label);
219           if (share.strName.empty())
220             share.strName = g_localizeStrings.Get(info.label);
221           locations.push_back(share);
222         }
223       }
224     }
225   }
226 }
227 
AddNetworkLocation(const std::string & path)228 bool CMediaManager::AddNetworkLocation(const std::string &path)
229 {
230   CNetworkLocation location;
231   location.path = path;
232   location.id = (int)m_locations.size();
233   m_locations.push_back(location);
234   return SaveSources();
235 }
236 
HasLocation(const std::string & path) const237 bool CMediaManager::HasLocation(const std::string& path) const
238 {
239   for (unsigned int i=0;i<m_locations.size();++i)
240   {
241     if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
242       return true;
243   }
244 
245   return false;
246 }
247 
248 
RemoveLocation(const std::string & path)249 bool CMediaManager::RemoveLocation(const std::string& path)
250 {
251   for (unsigned int i=0;i<m_locations.size();++i)
252   {
253     if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, path))
254     {
255       // prompt for sources, remove, cancel,
256       m_locations.erase(m_locations.begin()+i);
257       return SaveSources();
258     }
259   }
260 
261   return false;
262 }
263 
SetLocationPath(const std::string & oldPath,const std::string & newPath)264 bool CMediaManager::SetLocationPath(const std::string& oldPath, const std::string& newPath)
265 {
266   for (unsigned int i=0;i<m_locations.size();++i)
267   {
268     if (URIUtils::CompareWithoutSlashAtEnd(m_locations[i].path, oldPath))
269     {
270       m_locations[i].path = newPath;
271       return SaveSources();
272     }
273   }
274 
275   return false;
276 }
277 
AddAutoSource(const CMediaSource & share,bool bAutorun)278 void CMediaManager::AddAutoSource(const CMediaSource &share, bool bAutorun)
279 {
280   CMediaSourceSettings::GetInstance().AddShare("files", share);
281   CMediaSourceSettings::GetInstance().AddShare("video", share);
282   CMediaSourceSettings::GetInstance().AddShare("pictures", share);
283   CMediaSourceSettings::GetInstance().AddShare("music", share);
284   CMediaSourceSettings::GetInstance().AddShare("programs", share);
285   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
286   CGUIComponent *gui = CServiceBroker::GetGUI();
287   if (gui)
288     gui->GetWindowManager().SendThreadMessage( msg );
289 
290 #ifdef HAS_DVD_DRIVE
291   if(bAutorun)
292     MEDIA_DETECT::CAutorun::ExecuteAutorun(share.strPath);
293 #endif
294 }
295 
RemoveAutoSource(const CMediaSource & share)296 void CMediaManager::RemoveAutoSource(const CMediaSource &share)
297 {
298   CMediaSourceSettings::GetInstance().DeleteSource("files", share.strName, share.strPath, true);
299   CMediaSourceSettings::GetInstance().DeleteSource("video", share.strName, share.strPath, true);
300   CMediaSourceSettings::GetInstance().DeleteSource("pictures", share.strName, share.strPath, true);
301   CMediaSourceSettings::GetInstance().DeleteSource("music", share.strName, share.strPath, true);
302   CMediaSourceSettings::GetInstance().DeleteSource("programs", share.strName, share.strPath, true);
303   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_SOURCES);
304   CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage( msg );
305 
306 #ifdef HAS_DVD_DRIVE
307   // delete cached CdInfo if any
308   RemoveCdInfo(TranslateDevicePath(share.strPath, true));
309   RemoveDiscInfo(TranslateDevicePath(share.strPath, true));
310 #endif
311 }
312 
313 /////////////////////////////////////////////////////////////
314 // AutoSource status functions:
315 //! @todo translate cdda://<device>/
316 
TranslateDevicePath(const std::string & devicePath,bool bReturnAsDevice)317 std::string CMediaManager::TranslateDevicePath(const std::string& devicePath, bool bReturnAsDevice)
318 {
319   CSingleLock waitLock(m_muAutoSource);
320   std::string strDevice = devicePath;
321   // fallback for cdda://local/ and empty devicePath
322 #ifdef HAS_DVD_DRIVE
323   if(devicePath.empty() || StringUtils::StartsWith(devicePath, "cdda://local"))
324     strDevice = m_strFirstAvailDrive;
325 #endif
326 
327 #ifdef TARGET_WINDOWS
328   if(!m_bhasoptical)
329     return "";
330 
331   if(bReturnAsDevice == false)
332     StringUtils::Replace(strDevice, "\\\\.\\","");
333   else if(!strDevice.empty() && strDevice[1]==':')
334     strDevice = StringUtils::Format("\\\\.\\%c:", strDevice[0]);
335 
336   URIUtils::RemoveSlashAtEnd(strDevice);
337 #endif
338   return strDevice;
339 }
340 
IsDiscInDrive(const std::string & devicePath)341 bool CMediaManager::IsDiscInDrive(const std::string& devicePath)
342 {
343 #ifdef HAS_DVD_DRIVE
344 #ifdef TARGET_WINDOWS
345   if(!m_bhasoptical)
346     return false;
347 
348   std::string strDevice = TranslateDevicePath(devicePath, false);
349   std::map<std::string,CCdInfo*>::iterator it;
350   CSingleLock waitLock(m_muAutoSource);
351   it = m_mapCdInfo.find(strDevice);
352   if(it != m_mapCdInfo.end())
353     return true;
354   else
355     return false;
356 #else
357   if(URIUtils::IsDVD(devicePath) || devicePath.empty())
358     return MEDIA_DETECT::CDetectDVDMedia::IsDiscInDrive();   //! @todo switch all ports to use auto sources
359   else
360     return true; // Assume other paths to be mounted already
361 #endif
362 #else
363   return false;
364 #endif
365 }
366 
IsAudio(const std::string & devicePath)367 bool CMediaManager::IsAudio(const std::string& devicePath)
368 {
369 #ifdef HAS_DVD_DRIVE
370 #ifdef TARGET_WINDOWS
371   if(!m_bhasoptical)
372     return false;
373 
374   CCdInfo* pCdInfo = GetCdInfo(devicePath);
375   if(pCdInfo != NULL && pCdInfo->IsAudio(1))
376     return true;
377 
378   return false;
379 #else
380   //! @todo switch all ports to use auto sources
381   MEDIA_DETECT::CCdInfo* pInfo = MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
382   if (pInfo != NULL && pInfo->IsAudio(1))
383     return true;
384 #endif
385 #endif
386   return false;
387 }
388 
HasOpticalDrive()389 bool CMediaManager::HasOpticalDrive()
390 {
391 #ifdef HAS_DVD_DRIVE
392   if (!m_strFirstAvailDrive.empty())
393     return true;
394 #endif
395   return false;
396 }
397 
GetDriveStatus(const std::string & devicePath)398 DWORD CMediaManager::GetDriveStatus(const std::string& devicePath)
399 {
400 #ifdef HAS_DVD_DRIVE
401 #ifdef TARGET_WINDOWS
402   if(!m_bhasoptical)
403     return DRIVE_NOT_READY;
404 
405   std::string strDevice = TranslateDevicePath(devicePath, true);
406   DWORD dwRet = DRIVE_NOT_READY;
407   int status = CWIN32Util::GetDriveStatus(strDevice);
408 
409   switch(status)
410   {
411   case -1: // error
412     dwRet = DRIVE_NOT_READY;
413     break;
414   case 0: // no media
415     dwRet = DRIVE_CLOSED_NO_MEDIA;
416     break;
417   case 1: // tray open
418     dwRet = DRIVE_OPEN;
419     break;
420   case 2: // media accessible
421     dwRet = DRIVE_CLOSED_MEDIA_PRESENT;
422     break;
423   }
424   return dwRet;
425 #else
426   return MEDIA_DETECT::CDetectDVDMedia::DriveReady();
427 #endif
428 #else
429   return DRIVE_NOT_READY;
430 #endif
431 }
432 
433 #ifdef HAS_DVD_DRIVE
GetCdInfo(const std::string & devicePath)434 CCdInfo* CMediaManager::GetCdInfo(const std::string& devicePath)
435 {
436 #ifdef TARGET_WINDOWS
437   if(!m_bhasoptical)
438     return NULL;
439 
440   std::string strDevice = TranslateDevicePath(devicePath, false);
441   std::map<std::string,CCdInfo*>::iterator it;
442   {
443     CSingleLock waitLock(m_muAutoSource);
444     it = m_mapCdInfo.find(strDevice);
445     if(it != m_mapCdInfo.end())
446       return it->second;
447   }
448 
449   CCdInfo* pCdInfo=NULL;
450   CCdIoSupport cdio;
451   pCdInfo = cdio.GetCdInfo((char*)strDevice.c_str());
452   if(pCdInfo!=NULL)
453   {
454     CSingleLock waitLock(m_muAutoSource);
455     m_mapCdInfo.insert(std::pair<std::string,CCdInfo*>(strDevice,pCdInfo));
456   }
457 
458   return pCdInfo;
459 #else
460   return MEDIA_DETECT::CDetectDVDMedia::GetCdInfo();
461 #endif
462 }
463 
RemoveCdInfo(const std::string & devicePath)464 bool CMediaManager::RemoveCdInfo(const std::string& devicePath)
465 {
466   if(!m_bhasoptical)
467     return false;
468 
469   std::string strDevice = TranslateDevicePath(devicePath, false);
470 
471   std::map<std::string,CCdInfo*>::iterator it;
472   CSingleLock waitLock(m_muAutoSource);
473   it = m_mapCdInfo.find(strDevice);
474   if(it != m_mapCdInfo.end())
475   {
476     if(it->second != NULL)
477       delete it->second;
478 
479     m_mapCdInfo.erase(it);
480     return true;
481   }
482   return false;
483 }
484 
GetDiskLabel(const std::string & devicePath)485 std::string CMediaManager::GetDiskLabel(const std::string& devicePath)
486 {
487 #ifdef TARGET_WINDOWS_STORE
488   return ""; // GetVolumeInformationW nut support in UWP app
489 #elif defined(TARGET_WINDOWS)
490   if(!m_bhasoptical)
491     return "";
492 
493   std::string mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
494 
495   auto cached = m_mapDiscInfo.find(mediaPath);
496   if (cached != m_mapDiscInfo.end())
497     return cached->second.name;
498 
499   // try to minimize the chance of a "device not ready" dialog
500   std::string drivePath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath, true);
501   if (CServiceBroker::GetMediaManager().GetDriveStatus(drivePath) != DRIVE_CLOSED_MEDIA_PRESENT)
502     return "";
503 
504   DiscInfo info;
505   info = GetDiscInfo(mediaPath);
506   if (!info.name.empty())
507   {
508     m_mapDiscInfo[mediaPath] = info;
509     return info.name;
510   }
511 
512   std::string strDevice = TranslateDevicePath(devicePath);
513   WCHAR cVolumenName[128];
514   WCHAR cFSName[128];
515   URIUtils::AddSlashAtEnd(strDevice);
516   std::wstring strDeviceW;
517   g_charsetConverter.utf8ToW(strDevice, strDeviceW);
518   if(GetVolumeInformationW(strDeviceW.c_str(), cVolumenName, 127, NULL, NULL, NULL, cFSName, 127)==0)
519     return "";
520   g_charsetConverter.wToUTF8(cVolumenName, strDevice);
521   info.name = StringUtils::TrimRight(strDevice, " ");
522   if (!info.name.empty())
523     m_mapDiscInfo[mediaPath] = info;
524 
525   return info.name;
526 #else
527   return MEDIA_DETECT::CDetectDVDMedia::GetDVDLabel();
528 #endif
529 }
530 
GetDiskUniqueId(const std::string & devicePath)531 std::string CMediaManager::GetDiskUniqueId(const std::string& devicePath)
532 {
533   std::string mediaPath;
534 
535   CCdInfo* pInfo = CServiceBroker::GetMediaManager().GetCdInfo(devicePath);
536   if (pInfo == NULL)
537     return "";
538 
539   if (mediaPath.empty() && pInfo->IsAudio(1))
540     mediaPath = "cdda://local/";
541 
542   if (mediaPath.empty() && (pInfo->IsISOUDF(1) || pInfo->IsISOHFS(1) || pInfo->IsIso9660(1) || pInfo->IsIso9660Interactive(1)))
543     mediaPath = "iso9660://";
544 
545   if (mediaPath.empty())
546     mediaPath = devicePath;
547 
548 #ifdef TARGET_WINDOWS
549   if (mediaPath.empty() || mediaPath == "iso9660://")
550   {
551     mediaPath = CServiceBroker::GetMediaManager().TranslateDevicePath(devicePath);
552   }
553 #endif
554 
555   DiscInfo info = GetDiscInfo(mediaPath);
556   if (info.empty())
557   {
558     CLog::Log(LOGDEBUG, "GetDiskUniqueId: Retrieving ID for path %s failed, ID is empty.", CURL::GetRedacted(mediaPath).c_str());
559     return "";
560   }
561 
562   std::string strID = StringUtils::Format("removable://%s_%s", info.name.c_str(), info.serial.c_str());
563   CLog::Log(LOGDEBUG, "GetDiskUniqueId: Got ID %s for %s with path %s", strID.c_str(), info.type.c_str(), CURL::GetRedacted(mediaPath).c_str());
564 
565   return strID;
566 }
567 
GetDiscPath()568 std::string CMediaManager::GetDiscPath()
569 {
570 #ifdef TARGET_WINDOWS
571   return CServiceBroker::GetMediaManager().TranslateDevicePath("");
572 #else
573 
574   CSingleLock lock(m_CritSecStorageProvider);
575   VECSOURCES drives;
576   m_platformStorage->GetRemovableDrives(drives);
577   for(unsigned i = 0; i < drives.size(); ++i)
578   {
579     if(drives[i].m_iDriveType == CMediaSource::SOURCE_TYPE_DVD && !drives[i].strPath.empty())
580       return drives[i].strPath;
581   }
582 
583   // iso9660://, cdda://local/ or D:\ depending on disc type
584   return MEDIA_DETECT::CDetectDVDMedia::GetDVDPath();
585 #endif
586 }
587 #endif
588 
SetHasOpticalDrive(bool bstatus)589 void CMediaManager::SetHasOpticalDrive(bool bstatus)
590 {
591   CSingleLock waitLock(m_muAutoSource);
592   m_bhasoptical = bstatus;
593 }
594 
Eject(const std::string & mountpath)595 bool CMediaManager::Eject(const std::string& mountpath)
596 {
597   CSingleLock lock(m_CritSecStorageProvider);
598   return m_platformStorage->Eject(mountpath);
599 }
600 
EjectTray(const bool bEject,const char cDriveLetter)601 void CMediaManager::EjectTray( const bool bEject, const char cDriveLetter )
602 {
603 #ifdef HAS_DVD_DRIVE
604 #ifdef TARGET_WINDOWS
605   CWIN32Util::EjectTray(cDriveLetter);
606 #else
607   std::shared_ptr<CLibcdio> c_cdio = CLibcdio::GetInstance();
608   char* dvdDevice = c_cdio->GetDeviceFileName();
609   int nRetries=3;
610   while (nRetries-- > 0)
611   {
612     CdIo_t* cdio = c_cdio->cdio_open(dvdDevice, DRIVER_UNKNOWN);
613     if (cdio)
614     {
615       c_cdio->cdio_eject_media(&cdio);
616       c_cdio->cdio_destroy(cdio);
617     }
618     else
619       break;
620   }
621 #endif
622 #endif
623 }
624 
CloseTray(const char cDriveLetter)625 void CMediaManager::CloseTray(const char cDriveLetter)
626 {
627 #ifdef HAS_DVD_DRIVE
628 #if defined(TARGET_DARWIN)
629   // FIXME...
630 #elif (defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY))
631   // NYI
632 #elif defined(TARGET_POSIX)
633   char* dvdDevice = CLibcdio::GetInstance()->GetDeviceFileName();
634   if (strlen(dvdDevice) != 0)
635   {
636     int fd = open(dvdDevice, O_RDONLY | O_NONBLOCK);
637     if (fd >= 0)
638     {
639       ioctl(fd, CDROMCLOSETRAY, 0);
640       close(fd);
641     }
642   }
643 #elif defined(TARGET_WINDOWS)
644   CWIN32Util::CloseTray(cDriveLetter);
645 #endif
646 #endif
647 }
648 
ToggleTray(const char cDriveLetter)649 void CMediaManager::ToggleTray(const char cDriveLetter)
650 {
651 #ifdef HAS_DVD_DRIVE
652 #if defined(TARGET_WINDOWS)
653   CWIN32Util::ToggleTray(cDriveLetter);
654 #else
655   if (GetDriveStatus() == TRAY_OPEN || GetDriveStatus() == DRIVE_OPEN)
656     CloseTray();
657   else
658     EjectTray();
659 #endif
660 #endif
661 }
662 
ProcessEvents()663 void CMediaManager::ProcessEvents()
664 {
665   CSingleLock lock(m_CritSecStorageProvider);
666   if (m_platformStorage->PumpDriveChangeEvents(this))
667   {
668 #if defined(HAS_DVD_DRIVE) && defined(TARGET_DARWIN_OSX)
669     // darwins GetFirstOpticalDeviceFileName only gives us something
670     // when a disc is inserted
671     // so we have to refresh m_strFirstAvailDrive when this happens after Initialize
672     // was called (e.x. the disc was inserted after the start of xbmc)
673     // else TranslateDevicePath wouldn't give the correct device
674     m_strFirstAvailDrive = m_platformStorage->GetFirstOpticalDeviceFileName();
675 #endif
676 
677     CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
678     CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
679   }
680 }
681 
GetDiskUsage()682 std::vector<std::string> CMediaManager::GetDiskUsage()
683 {
684   CSingleLock lock(m_CritSecStorageProvider);
685   return m_platformStorage->GetDiskUsage();
686 }
687 
OnStorageAdded(const std::string & label,const std::string & path)688 void CMediaManager::OnStorageAdded(const std::string &label, const std::string &path)
689 {
690 #ifdef HAS_DVD_DRIVE
691   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
692   if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) != AUTOCD_NONE || settings->GetBool(CSettings::SETTING_DVDS_AUTORUN))
693     if (settings->GetInt(CSettings::SETTING_AUDIOCDS_AUTOACTION) == AUTOCD_RIP)
694       CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_LOW);
695     else
696       CJobManager::GetInstance().AddJob(new CAutorunMediaJob(label, path), this, CJob::PRIORITY_HIGH);
697   else
698     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13021), label, TOAST_DISPLAY_TIME, false);
699 #endif
700 }
701 
OnStorageSafelyRemoved(const std::string & label)702 void CMediaManager::OnStorageSafelyRemoved(const std::string &label)
703 {
704   CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13023), label, TOAST_DISPLAY_TIME, false);
705 }
706 
OnStorageUnsafelyRemoved(const std::string & label)707 void CMediaManager::OnStorageUnsafelyRemoved(const std::string &label)
708 {
709   CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13022), label);
710 }
711 
GetDiscInfo(const std::string & mediaPath)712 CMediaManager::DiscInfo CMediaManager::GetDiscInfo(const std::string& mediaPath)
713 {
714   DiscInfo info;
715 
716   if (mediaPath.empty())
717     return info;
718 
719   // Try finding VIDEO_TS/VIDEO_TS.IFO - this indicates a DVD disc is inserted
720   std::string pathVideoTS = URIUtils::AddFileToFolder(mediaPath, "VIDEO_TS", "VIDEO_TS.IFO");
721   // correct the filename if needed
722   if (StringUtils::StartsWith(mediaPath, "dvd://") ||
723       StringUtils::StartsWith(mediaPath, "iso9660://"))
724   {
725     pathVideoTS = TranslateDevicePath("");
726   }
727 
728   if (XFILE::CFile::Exists(pathVideoTS))
729   {
730     CFileItem item(pathVideoTS, false);
731     CDVDInputStreamNavigator dvdNavigator(nullptr, item);
732     if (dvdNavigator.Open())
733     {
734       info.type = "DVD";
735       info.name = dvdNavigator.GetDVDTitleString();
736       info.serial = dvdNavigator.GetDVDSerialString();
737       return info;
738     }
739   }
740 #ifdef HAVE_LIBBLURAY
741   // check for Blu-ray discs
742   if (XFILE::CFile::Exists(URIUtils::AddFileToFolder(mediaPath, "BDMV", "index.bdmv")))
743   {
744     info.type = "Blu-ray";
745     CBlurayDirectory bdDir;
746 
747     if (!bdDir.InitializeBluray(mediaPath))
748       return info;
749 
750     info.name = bdDir.GetBlurayTitle();
751     info.serial = bdDir.GetBlurayID();
752   }
753 #endif
754 
755   return info;
756 }
757 
RemoveDiscInfo(const std::string & devicePath)758 void CMediaManager::RemoveDiscInfo(const std::string& devicePath)
759 {
760   std::string strDevice = TranslateDevicePath(devicePath, false);
761 
762   auto it = m_mapDiscInfo.find(strDevice);
763   if (it != m_mapDiscInfo.end())
764     m_mapDiscInfo.erase(it);
765 }
766 
playStubFile(const CFileItem & item)767 bool CMediaManager::playStubFile(const CFileItem& item)
768 {
769   // Figure out Lines 1 and 2 of the dialog
770   std::string strLine1, strLine2;
771 
772   // use generic message by default
773   strLine1 = g_localizeStrings.Get(435).c_str();
774   strLine2 = g_localizeStrings.Get(436).c_str();
775 
776   CXBMCTinyXML discStubXML;
777   if (discStubXML.LoadFile(item.GetPath()))
778   {
779     TiXmlElement* pRootElement = discStubXML.RootElement();
780     if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "discstub") != 0)
781       CLog::Log(LOGINFO, "No <discstub> node found for %s. Using default info dialog message", item.GetPath().c_str());
782     else
783     {
784       XMLUtils::GetString(pRootElement, "title", strLine1);
785       XMLUtils::GetString(pRootElement, "message", strLine2);
786       // no title? use the label of the CFileItem as line 1
787       if (strLine1.empty())
788         strLine1 = item.GetLabel();
789     }
790   }
791 
792   if (HasOpticalDrive())
793   {
794 #ifdef HAS_DVD_DRIVE
795     if (CGUIDialogPlayEject::ShowAndGetInput(strLine1, strLine2))
796       return MEDIA_DETECT::CAutorun::PlayDiscAskResume();
797 #endif
798   }
799   else
800   {
801     KODI::MESSAGING::HELPERS::ShowOKDialogText(strLine1, strLine2);
802   }
803   return true;
804 }
805