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 #include "Win10StorageProvider.h"
9 
10 #include "ServiceBroker.h"
11 #include "filesystem/SpecialProtocol.h"
12 #include "guilib/LocalizeStrings.h"
13 #include "storage/MediaManager.h"
14 #include "utils/JobManager.h"
15 #include "utils/StringUtils.h"
16 #include "utils/log.h"
17 
18 #include "platform/win10/AsyncHelpers.h"
19 #include "platform/win10/filesystem/WinLibraryDirectory.h"
20 #include "platform/win32/CharsetConverter.h"
21 
22 #include <winrt/Windows.Devices.Enumeration.h>
23 #include <winrt/Windows.Foundation.Collections.h>
24 #include <winrt/Windows.Storage.h>
25 
26 namespace winrt
27 {
28   using namespace Windows::Foundation;
29 }
30 using namespace winrt::Windows::Devices::Enumeration;
31 using namespace winrt::Windows::Foundation::Collections;
32 using namespace winrt::Windows::Storage;
33 
CreateInstance()34 ::IStorageProvider* ::IStorageProvider::CreateInstance()
35 {
36   return new CStorageProvider();
37 }
38 
~CStorageProvider()39 CStorageProvider::~CStorageProvider()
40 {
41   if (m_watcher && m_watcher.Status() == DeviceWatcherStatus::Started)
42     m_watcher.Stop();
43 }
44 
Initialize()45 void CStorageProvider::Initialize()
46 {
47   m_changed = false;
48   VECSOURCES vShare;
49   GetDrivesByType(vShare, DVD_DRIVES);
50   if (!vShare.empty())
51     CServiceBroker::GetMediaManager().SetHasOpticalDrive(true);
52   else
53     CLog::Log(LOGDEBUG, "%s: No optical drive found.", __FUNCTION__);
54 
55   m_watcher = DeviceInformation::CreateWatcher(DeviceClass::PortableStorageDevice);
56   m_watcher.Added([this](auto&&, auto&&)
57   {
58     m_changed = true;
59   });
60   m_watcher.Removed([this](auto&&, auto&&)
61   {
62     m_changed = true;
63   });
64   m_watcher.Updated([this](auto&&, auto&&)
65   {
66     m_changed = true;
67   });
68   m_watcher.Start();
69 }
70 
GetLocalDrives(VECSOURCES & localDrives)71 void CStorageProvider::GetLocalDrives(VECSOURCES &localDrives)
72 {
73   CMediaSource share;
74   share.strPath = CSpecialProtocol::TranslatePath("special://home");
75   share.strName = g_localizeStrings.Get(21440);
76   share.m_ignore = true;
77   share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
78   localDrives.push_back(share);
79 
80   GetDrivesByType(localDrives, LOCAL_DRIVES, true);
81 }
82 
GetRemovableDrives(VECSOURCES & removableDrives)83 void CStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
84 {
85   using KODI::PLATFORM::WINDOWS::FromW;
86 
87   // get drives which we have direct access for (in case of broad file system access)
88   GetDrivesByType(removableDrives, REMOVABLE_DRIVES, true);
89 
90   try
91   {
92     auto devicesView = Wait(winrt::Windows::Storage::KnownFolders::RemovableDevices().GetFoldersAsync());
93     for (unsigned i = 0; i < devicesView.Size(); i++)
94     {
95       auto device = devicesView.GetAt(i);
96       if (device.Path().empty())
97         continue;
98       CMediaSource source;
99       source.strName = FromW(device.DisplayName().c_str());
100       std::string driveLetter = FromW(device.Path().c_str());
101 
102       // skip exiting in case if we have direct access
103       auto exiting = std::find_if(removableDrives.begin(), removableDrives.end(), [&driveLetter](CMediaSource& m) {
104         return m.strPath == driveLetter;
105       });
106       if (exiting != removableDrives.end())
107         continue;
108 
109       UINT uDriveType = GetDriveTypeA(driveLetter.c_str());
110       source.strPath = "win-lib://removable/" + driveLetter + "/";
111       source.m_iDriveType = (
112         (uDriveType == DRIVE_FIXED) ? CMediaSource::SOURCE_TYPE_LOCAL :
113         (uDriveType == DRIVE_REMOTE) ? CMediaSource::SOURCE_TYPE_REMOTE :
114         (uDriveType == DRIVE_CDROM) ? CMediaSource::SOURCE_TYPE_DVD :
115         (uDriveType == DRIVE_REMOVABLE) ? CMediaSource::SOURCE_TYPE_REMOVABLE :
116         CMediaSource::SOURCE_TYPE_UNKNOWN);
117 
118       removableDrives.push_back(source);
119     }
120   }
121   catch (const winrt::hresult_error&)
122   {
123   }
124 }
125 
GetFirstOpticalDeviceFileName()126 std::string CStorageProvider::GetFirstOpticalDeviceFileName()
127 {
128   VECSOURCES vShare;
129   std::string strdevice = "\\\\.\\";
130   GetDrivesByType(vShare, DVD_DRIVES);
131 
132   if (!vShare.empty())
133     return strdevice.append(vShare.front().strPath);
134   else
135     return "";
136 }
137 
Eject(const std::string & mountpath)138 bool CStorageProvider::Eject(const std::string& mountpath)
139 {
140   return false;
141 }
142 
GetDiskUsage()143 std::vector<std::string> CStorageProvider::GetDiskUsage()
144 {
145   using KODI::PLATFORM::WINDOWS::FromW;
146 
147   std::vector<std::string> result;
148   ULARGE_INTEGER ULTotal = { { 0 } };
149   ULARGE_INTEGER ULTotalFree = { { 0 } };
150   std::string strRet;
151 
152   auto localfolder = ApplicationData::Current().LocalFolder().Path();
153   GetDiskFreeSpaceExW(localfolder.c_str(), nullptr, &ULTotal, &ULTotalFree);
154   strRet = StringUtils::Format("%s: %d MB %s", g_localizeStrings.Get(21440),
155                                (ULTotalFree.QuadPart / (1024 * 1024)),
156                                g_localizeStrings.Get(160).c_str());
157   result.push_back(strRet);
158 
159   DWORD drivesBits = GetLogicalDrives();
160   if (drivesBits == 0)
161     return result;
162 
163   CMediaSource share;
164 
165   drivesBits >>= 2;       // skip A and B
166   char driveLetter = 'C'; // start with C
167   for (; drivesBits > 0; drivesBits >>= 1, driveLetter++)
168   {
169     if (!(drivesBits & 1))
170       continue;
171 
172     std::string strDrive = std::string(1, driveLetter) + ":\\";
173     if (DRIVE_FIXED == GetDriveTypeA(strDrive.c_str())
174       && GetDiskFreeSpaceExA((strDrive.c_str()), nullptr, &ULTotal, &ULTotalFree))
175     {
176       strRet = StringUtils::Format("%s %d MB %s", strDrive.c_str(), int(ULTotalFree.QuadPart / (1024 * 1024)), g_localizeStrings.Get(160).c_str());
177       result.push_back(strRet);
178     }
179   }
180   return result;
181 }
182 
PumpDriveChangeEvents(IStorageEventsCallback * callback)183 bool CStorageProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback)
184 {
185   bool res = m_changed.load();
186   m_changed = false;
187   return res;
188 }
189 
GetDrivesByType(VECSOURCES & localDrives,Drive_Types eDriveType,bool bonlywithmedia)190 void CStorageProvider::GetDrivesByType(VECSOURCES & localDrives, Drive_Types eDriveType, bool bonlywithmedia)
191 {
192   DWORD drivesBits = GetLogicalDrives();
193   if (drivesBits == 0)
194     return;
195 
196   CMediaSource share;
197   char volumeName[100];
198 
199   drivesBits >>= 2;       // skip A and B
200   char driveLetter = 'C'; // start with C
201   for (; drivesBits > 0; drivesBits >>= 1, driveLetter++)
202   {
203     if (!(drivesBits & 1))
204       continue;
205 
206     std::string strDrive = std::string(1, driveLetter) + ":\\";
207     UINT uDriveType = GetDriveTypeA(strDrive.c_str());
208     int nResult = GetVolumeInformationA(strDrive.c_str(), volumeName, 100, 0, 0, 0, NULL, 25);
209     if (nResult == 0 && bonlywithmedia)
210     {
211       continue;
212     }
213 
214     bool bUseDCD = false;
215     // skip unsupported types
216     if (uDriveType < DRIVE_REMOVABLE || uDriveType > DRIVE_CDROM)
217       continue;
218     // only fixed and remote
219     if (eDriveType == LOCAL_DRIVES && uDriveType != DRIVE_FIXED && uDriveType != DRIVE_REMOTE)
220       continue;
221     // only removable
222     if (eDriveType == REMOVABLE_DRIVES && uDriveType != DRIVE_REMOVABLE)
223       continue;
224     // only CD-ROMs
225     if (eDriveType == DVD_DRIVES && uDriveType != DRIVE_CDROM)
226       continue;
227 
228     share.strPath = strDrive;
229     if (volumeName[0] != L'\0')
230       share.strName = volumeName;
231     if (uDriveType == DRIVE_CDROM && nResult)
232     {
233       // Has to be the same as auto mounted devices
234       share.strStatus = share.strName;
235       share.strName = share.strPath;
236       share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
237       bUseDCD = true;
238     }
239     else
240     {
241       // Lets show it, like Windows explorer do...
242       switch (uDriveType)
243       {
244       case DRIVE_CDROM:
245         share.strName = StringUtils::Format("%s (%s)", share.strPath.c_str(), g_localizeStrings.Get(218).c_str());
246         break;
247       case DRIVE_REMOVABLE:
248         if (share.strName.empty())
249           share.strName = StringUtils::Format("%s (%s)", g_localizeStrings.Get(437).c_str(), share.strPath.c_str());
250         break;
251       default:
252         if (share.strName.empty())
253           share.strName = share.strPath;
254         else
255           share.strName = StringUtils::Format("%s (%s)", share.strPath.c_str(), share.strName.c_str());
256         break;
257       }
258     }
259 
260     StringUtils::Replace(share.strName, ":\\", ":");
261     StringUtils::Replace(share.strPath, ":\\", ":");
262     share.m_ignore = true;
263     if (!bUseDCD)
264     {
265       share.m_iDriveType = (
266         (uDriveType == DRIVE_FIXED) ? CMediaSource::SOURCE_TYPE_LOCAL :
267         (uDriveType == DRIVE_REMOTE) ? CMediaSource::SOURCE_TYPE_REMOTE :
268         (uDriveType == DRIVE_CDROM) ? CMediaSource::SOURCE_TYPE_DVD :
269         (uDriveType == DRIVE_REMOVABLE) ? CMediaSource::SOURCE_TYPE_REMOVABLE :
270         CMediaSource::SOURCE_TYPE_UNKNOWN);
271     }
272 
273     AddOrReplace(localDrives, share);
274   }
275 }
276 
277