Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / storage / android / AndroidStorageProvider.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "AndroidStorageProvider.h"
22 #include "android/activity/XBMCApp.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "filesystem/File.h"
25
26 #include "utils/log.h"
27 #include "utils/RegExp.h"
28 #include "utils/StringUtils.h"
29 #include "utils/URIUtils.h"
30 #include <cstdio>
31 #include <cstring>
32 #include <cstdlib>
33 #include <map>
34
35 CAndroidStorageProvider::CAndroidStorageProvider()
36 {
37   m_removableLength = 0;
38   PumpDriveChangeEvents(NULL);
39 }
40
41 std::string CAndroidStorageProvider::unescape(const std::string& str)
42 {
43   std::string retString;
44   for (uint32_t i=0; i < str.length(); ++i)
45   {
46     if (str[i] != '\\')
47       retString += str[i];
48     else
49     {
50       i += 1;
51       if (str[i] == 'u') // unicode
52       {
53         // TODO
54       }
55       else if (str[i] >= '0' && str[i] <= '7') // octal
56       {
57         std::string octString;
58         while (str[i] >= '0' && str[i] <= '7')
59         {
60           octString += str[i];
61           i += 1;
62         }
63         if (octString.length() != 0)
64         {
65           uint8_t val = 0;
66           for (int j=octString.length()-1; j>=0; --j)
67           {
68             val += ((uint8_t)(octString[j] - '0')) * (1 << ((octString.length() - (j+1)) * 3));
69           }
70           retString += (char)val;
71           i -= 1;
72         }
73       }
74     }
75   }
76   return retString;
77 }
78
79 void CAndroidStorageProvider::GetLocalDrives(VECSOURCES &localDrives)
80 {
81   CMediaSource share;
82
83   // external directory
84   std::string path;
85   if (CXBMCApp::GetExternalStorage(path) && !path.empty()  && XFILE::CFile::Exists(path))
86   {
87     share.strPath = path;
88     share.strName = g_localizeStrings.Get(21456);
89     share.m_ignore = true;
90     localDrives.push_back(share);
91   }
92
93   // root directory
94   share.strPath = "/";
95   share.strName = g_localizeStrings.Get(21453);
96   localDrives.push_back(share);
97 }
98
99 void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
100 {
101   // mounted usb disks
102   char*                               buf     = NULL;
103   FILE*                               pipe;
104   std::map<std::string, std::string>  result;
105   CRegExp                             reMount;
106   reMount.RegComp("^(.+?)\\s+(.+?)\\s+(.+?)\\s");
107
108   /* /proc/mounts is only guaranteed atomic for the current read
109    * operation, so we need to read it all at once.
110    */
111   if ((pipe = fopen("/proc/mounts", "r")))
112   {
113     char*   new_buf;
114     size_t  buf_len = 4096;
115
116     while ((new_buf = (char*)realloc(buf, buf_len * sizeof(char))))
117     {
118       size_t nread;
119
120       buf   = new_buf;
121       nread = fread(buf, sizeof(char), buf_len, pipe);
122
123       if (nread == buf_len)
124       {
125         rewind(pipe);
126         buf_len *= 2;
127       }
128       else
129       {
130         buf[nread] = '\0';
131         if (!feof(pipe))
132           new_buf = NULL;
133         break;
134       }
135     }
136
137     if (!new_buf)
138     {
139       free(buf);
140       buf = NULL;
141     }
142     fclose(pipe);
143   }
144   else
145     CLog::Log(LOGERROR, "Cannot read mount points");
146
147   if (buf)
148   {
149     char* line;
150     char* saveptr = NULL;
151
152     line = strtok_r(buf, "\n", &saveptr);
153
154     while (line)
155     {
156       if (reMount.RegFind(line) != -1)
157       {
158         bool accepted = false;
159         std::string device   = reMount.GetReplaceString("\\1");
160         std::string mountStr = reMount.GetReplaceString("\\2");
161         std::string fsStr    = reMount.GetReplaceString("\\3");
162         const char* fs    = fsStr.c_str();
163
164         // Here we choose which filesystems are approved
165         if (strcmp(fs, "fuseblk") == 0 || strcmp(fs, "vfat") == 0
166             || strcmp(fs, "ext2") == 0 || strcmp(fs, "ext3") == 0 || strcmp(fs, "ext4") == 0
167             || strcmp(fs, "reiserfs") == 0 || strcmp(fs, "xfs") == 0
168             || strcmp(fs, "ntfs-3g") == 0 || strcmp(fs, "iso9660") == 0
169             || strcmp(fs, "exfat") == 0
170             || strcmp(fs, "fusefs") == 0 || strcmp(fs, "hfs") == 0)
171           accepted = true;
172
173         // Ignore sdcards
174         if (!StringUtils::StartsWith(device, "/dev/block/vold/") ||
175             mountStr.find("sdcard") != std::string::npos)
176           accepted = false;
177
178         if(accepted)
179           result[device] = mountStr;
180       }
181       line = strtok_r(NULL, "\n", &saveptr);
182     }
183     free(buf);
184   }
185
186   for (std::map<std::string, std::string>::const_iterator i = result.begin(); i != result.end(); ++i)
187   {
188     CMediaSource share;
189     share.strPath = unescape(i->second);
190     share.strName = URIUtils::GetFileName(share.strPath);
191     share.m_ignore = true;
192     removableDrives.push_back(share);
193   }
194 }
195
196 std::vector<CStdString> CAndroidStorageProvider::GetDiskUsage()
197 {
198   std::vector<CStdString> result;
199
200   std::string usage;
201   // add header
202   CXBMCApp::GetStorageUsage("", usage);
203   result.push_back(usage);
204
205   usage.clear();
206   // add rootfs
207   if (CXBMCApp::GetStorageUsage("/", usage) && !usage.empty())
208     result.push_back(usage);
209
210   usage.clear();
211   // add external storage if available
212   std::string path;
213   if (CXBMCApp::GetExternalStorage(path) && !path.empty() &&
214       CXBMCApp::GetStorageUsage(path, usage) && !usage.empty())
215     result.push_back(usage);
216
217   // add removable storage
218   VECSOURCES drives;
219   GetRemovableDrives(drives);
220   for (unsigned int i = 0; i < drives.size(); i++)
221   {
222     usage.clear();
223     if (CXBMCApp::GetStorageUsage(drives[i].strPath, usage) && !usage.empty())
224       result.push_back(usage);
225   }
226
227   return result;
228 }
229
230 bool CAndroidStorageProvider::Eject(CStdString mountpath)
231 {
232   return false;
233 }
234
235 bool CAndroidStorageProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback)
236 {
237   VECSOURCES drives;
238   GetRemovableDrives(drives);
239   bool changed = drives.size() != m_removableLength;
240   m_removableLength = drives.size();
241   return changed;
242 }