Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / filesystem / AFPDirectory.cpp
1 /*
2  *      Copyright (C) 2011 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 "system.h"
23
24 #if defined(HAS_FILESYSTEM_AFP)
25 #include "AFPDirectory.h"
26 #include "FileAFP.h"
27 #include "Util.h"
28 #include "guilib/LocalizeStrings.h"
29 #include "Application.h"
30 #include "FileItem.h"
31 #include "settings/AdvancedSettings.h"
32 #include "utils/StringUtils.h"
33 #include "utils/log.h"
34 #include "utils/URIUtils.h"
35 #include "threads/SingleLock.h"
36 #include "PasswordManager.h"
37 #include "DllLibAfp.h"
38
39 struct CachedDirEntry
40 {
41   unsigned int type;
42   CStdString name;
43 };
44
45 using namespace XFILE;
46 using namespace std;
47
48 CAFPDirectory::CAFPDirectory(void)
49 {
50 }
51
52 CAFPDirectory::~CAFPDirectory(void)
53 {
54 }
55
56 bool CAFPDirectory::ResolveSymlink( const CStdString &dirName, const CStdString &fileName, 
57                                     struct stat *stat, CURL &resolvedUrl)
58 {
59   CSingleLock lock(gAfpConnection); 
60   int ret = 0;  
61   bool retVal = true;
62   char resolvedLink[MAX_PATH];
63   CStdString fullpath = dirName;
64   URIUtils::AddSlashAtEnd(fullpath);
65   fullpath += fileName;
66   
67   CPasswordManager::GetInstance().AuthenticateURL(resolvedUrl);
68   resolvedUrl.SetProtocol("afp");
69   resolvedUrl.SetHostName(gAfpConnection.GetConnectedIp());   
70   
71   ret = gAfpConnection.GetImpl()->afp_wrap_readlink(gAfpConnection.GetVolume(), fullpath.c_str(), resolvedLink, MAX_PATH);    
72   
73   if(ret == 0)
74   {
75     fullpath = dirName;
76     URIUtils::AddSlashAtEnd(fullpath);
77     fullpath.append(resolvedLink);
78  
79     if(resolvedLink[0] == '/')
80     {
81       //use the special stat function for using an extra context
82       //because we are inside of a dir traversation
83       //and just can't change the global nfs context here
84       //without destroying something...    
85       fullpath = resolvedLink;
86       fullpath = fullpath.Right(fullpath.length()-1);
87       resolvedUrl.SetFileName(fullpath);     
88       ret = gAfpConnection.stat(resolvedUrl, stat);
89       if(ret < 0)
90       {
91         URIUtils::AddSlashAtEnd(fullpath);
92         resolvedUrl.SetFileName(fullpath);     
93         ret = gAfpConnection.stat(resolvedUrl, stat);
94       }
95     }
96     else
97     {
98       ret = gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), fullpath.c_str(), stat);
99       resolvedUrl.SetFileName(gAfpConnection.GetUrl()->volumename + fullpath);            
100     }
101
102     if (ret != 0) 
103     {
104       CLog::Log(LOGERROR, "AFP: Failed to stat(%s) on link resolve %s\n", fullpath.c_str(), strerror(errno));
105       retVal = false;;
106     }
107   }
108   else
109   {
110     CLog::Log(LOGERROR, "Failed to readlink(%s) %s\n", fullpath.c_str(), strerror(errno));
111     retVal = false;
112   }
113   return retVal;
114 }
115
116
117 bool CAFPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
118 {
119   // We accept afp://[[user[:password@]]server[/share[/path[/file]]]]
120   // silence gdb breaking on signal SIGUSR2 with "handle SIGUSR2 nostop noprint"
121   bool bListVolumes = false;
122   FILETIME fileTime, localTime;
123
124   CSingleLock lock(gAfpConnection);
125   // we need an url to do proper escaping
126   CURL url(strPath);
127   CAfpConnection::afpConnnectError afpError = gAfpConnection.Connect(url);
128
129   if (afpError != CAfpConnection::AfpOk || (!url.GetShareName().IsEmpty() && !gAfpConnection.GetVolume()))
130   {
131     if (afpError == CAfpConnection::AfpAuth)
132     {
133        if (m_allowPrompting)
134        {
135          RequireAuthentication(url.Get());
136        }
137     }
138     return false;
139   }
140   CStdString strDirName = gAfpConnection.GetPath(url);
141
142   vector<CachedDirEntry> vecEntries;
143   struct afp_file_info *dirEnt = NULL;
144   struct afp_file_info *curDirPtr = NULL;
145
146   // if no share name in url - try to fetch the volumes on the server and treat them like folders
147   if (url.GetShareName().IsEmpty())
148   {
149     bListVolumes = true;
150     struct afp_server *serv = gAfpConnection.GetServer();
151     for (int i = 0; i < serv->num_volumes; i++)
152     {
153       CachedDirEntry aDir;
154       aDir.type = 1;
155       aDir.name = serv->volumes[i].volume_name;
156       vecEntries.push_back(aDir);
157     }
158   }
159
160   // if we not only list volumes - read the dir
161   if (!bListVolumes)
162   {
163     if (gAfpConnection.GetImpl()->afp_wrap_readdir(gAfpConnection.GetVolume(), strDirName.c_str(), &dirEnt))
164       return false;
165     lock.Leave();
166
167     for (curDirPtr = dirEnt; curDirPtr; curDirPtr = curDirPtr->next)
168     {
169       CachedDirEntry aDir;
170       aDir.type = curDirPtr->isdir;
171 #ifdef USE_CVS_AFPFS
172       aDir.name = curDirPtr->basic.name;
173 #else
174       aDir.name = curDirPtr->name;
175 #endif
176       vecEntries.push_back(aDir);
177     }
178     gAfpConnection.GetImpl()->afp_ml_filebase_free(&dirEnt);
179   }
180
181   for (size_t i = 0; i < vecEntries.size(); i++)
182   {
183     CachedDirEntry aDir = vecEntries[i];
184     // We use UTF-8 internally, as does AFP
185     CStdString strFile = aDir.name;
186     CStdString myStrPath(strPath);
187     URIUtils::AddSlashAtEnd(myStrPath); //be sure the dir ends with a slash    
188     CStdString path(myStrPath + strFile);
189
190     if (!strFile.Equals(".") && !strFile.Equals("..") && !strFile.Equals("lost+found"))
191     {
192       int64_t iSize = 0;
193       bool bIsDir = aDir.type;
194       int64_t lTimeDate = 0;
195
196       // if we not only list volumes - stat the files in folder
197       if (!bListVolumes)
198       {
199         struct stat info = {0};
200
201         if (m_extFileInfo && g_advancedSettings.m_sambastatfiles)
202         {
203           // make sure we use the authenticated path wich contains any default username
204           CStdString strFullName = strDirName + strFile;
205
206           lock.Enter();
207
208           if (gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), strFullName.c_str(), &info) == 0)
209           {                       
210             //resolve symlinks
211             if(S_ISLNK(info.st_mode))
212             {
213               CURL linkUrl(url);
214               if(!ResolveSymlink(strDirName, strFile, &info, linkUrl))
215               {
216                 lock.Leave();              
217                 continue;
218               }
219               path = linkUrl.Get();
220               bIsDir = info.st_mode & S_IFDIR;            
221             }
222             lTimeDate = info.st_mtime;
223             if (lTimeDate == 0) // if modification date is missing, use create date
224               lTimeDate = info.st_ctime;
225             iSize = info.st_size;
226           }
227           else
228           {
229             CLog::Log(LOGERROR, "%s - Failed to stat file %s (%s)", __FUNCTION__, strFullName.c_str(),strerror(errno));
230           }
231
232           lock.Leave();
233         }
234         LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
235         fileTime.dwLowDateTime  = (DWORD)(ll & 0xffffffff);
236         fileTime.dwHighDateTime = (DWORD)(ll >> 32);
237         FileTimeToLocalFileTime(&fileTime, &localTime);
238       }
239       else
240       {
241         bIsDir = true;
242         localTime.dwHighDateTime = 0;
243         localTime.dwLowDateTime = 0;
244       }
245       
246       CFileItemPtr pItem(new CFileItem(strFile));      
247       pItem->m_dateTime  = localTime;    
248       pItem->m_dwSize    = iSize;
249       
250       if (bIsDir)
251       {
252         URIUtils::AddSlashAtEnd(path);
253         pItem->m_bIsFolder = true;
254       }
255       else
256       {
257         pItem->m_bIsFolder = false;
258       }
259  
260       if (!aDir.name.empty() && aDir.name[0] == '.')
261       {
262         pItem->SetProperty("file:hidden", true);
263       }
264
265       pItem->SetPath(path);      
266       items.Add(pItem);      
267     }
268   }
269
270   return true;
271 }
272
273 bool CAFPDirectory::Create(const char* strPath)
274 {
275   CSingleLock lock(gAfpConnection);
276
277   CURL url(strPath);
278
279   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
280     return false;
281
282   CStdString strFilename = gAfpConnection.GetPath(url);
283
284   int result = gAfpConnection.GetImpl()->afp_wrap_mkdir(gAfpConnection.GetVolume(), strFilename.c_str(), 0);
285
286   if (result != 0)
287     CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
288
289   return (result == 0 || EEXIST == result);
290 }
291
292 bool CAFPDirectory::Remove(const char *strPath)
293 {
294   CSingleLock lock(gAfpConnection);
295
296   CURL url(strPath);
297   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
298     return false;
299
300   CStdString strFileName = gAfpConnection.GetPath(url);
301
302   int result = gAfpConnection.GetImpl()->afp_wrap_rmdir(gAfpConnection.GetVolume(), strFileName.c_str());
303
304   if (result != 0 && errno != ENOENT)
305   {
306     CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
307     return false;
308   }
309
310   return true;
311 }
312
313 bool CAFPDirectory::Exists(const char *strPath)
314 {
315   CSingleLock lock(gAfpConnection);
316
317   CURL url(strPath);
318   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
319     return false;
320
321   CStdString strFileName(gAfpConnection.GetPath(url));
322
323   struct stat info;
324   if (gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), strFileName.c_str(), &info) != 0)
325     return false;
326
327   return (info.st_mode & S_IFDIR) ? true : false;
328 }
329 #endif