2 * Copyright (C) 2011 Team XBMC
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)
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.
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
24 #if defined(HAS_FILESYSTEM_AFP)
25 #include "AFPDirectory.h"
28 #include "guilib/LocalizeStrings.h"
29 #include "Application.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"
45 using namespace XFILE;
48 CAFPDirectory::CAFPDirectory(void)
52 CAFPDirectory::~CAFPDirectory(void)
56 bool CAFPDirectory::ResolveSymlink( const CStdString &dirName, const CStdString &fileName,
57 struct stat *stat, CURL &resolvedUrl)
59 CSingleLock lock(gAfpConnection);
62 char resolvedLink[MAX_PATH];
63 CStdString fullpath = dirName;
64 URIUtils::AddSlashAtEnd(fullpath);
67 CPasswordManager::GetInstance().AuthenticateURL(resolvedUrl);
68 resolvedUrl.SetProtocol("afp");
69 resolvedUrl.SetHostName(gAfpConnection.GetConnectedIp());
71 ret = gAfpConnection.GetImpl()->afp_wrap_readlink(gAfpConnection.GetVolume(), fullpath.c_str(), resolvedLink, MAX_PATH);
76 URIUtils::AddSlashAtEnd(fullpath);
77 fullpath.append(resolvedLink);
79 if(resolvedLink[0] == '/')
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);
91 URIUtils::AddSlashAtEnd(fullpath);
92 resolvedUrl.SetFileName(fullpath);
93 ret = gAfpConnection.stat(resolvedUrl, stat);
98 ret = gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), fullpath.c_str(), stat);
99 resolvedUrl.SetFileName(gAfpConnection.GetUrl()->volumename + fullpath);
104 CLog::Log(LOGERROR, "AFP: Failed to stat(%s) on link resolve %s\n", fullpath.c_str(), strerror(errno));
110 CLog::Log(LOGERROR, "Failed to readlink(%s) %s\n", fullpath.c_str(), strerror(errno));
117 bool CAFPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
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;
124 CSingleLock lock(gAfpConnection);
125 // we need an url to do proper escaping
127 CAfpConnection::afpConnnectError afpError = gAfpConnection.Connect(url);
129 if (afpError != CAfpConnection::AfpOk || (!url.GetShareName().IsEmpty() && !gAfpConnection.GetVolume()))
131 if (afpError == CAfpConnection::AfpAuth)
133 if (m_allowPrompting)
135 RequireAuthentication(url.Get());
140 CStdString strDirName = gAfpConnection.GetPath(url);
142 vector<CachedDirEntry> vecEntries;
143 struct afp_file_info *dirEnt = NULL;
144 struct afp_file_info *curDirPtr = NULL;
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())
150 struct afp_server *serv = gAfpConnection.GetServer();
151 for (int i = 0; i < serv->num_volumes; i++)
155 aDir.name = serv->volumes[i].volume_name;
156 vecEntries.push_back(aDir);
160 // if we not only list volumes - read the dir
163 if (gAfpConnection.GetImpl()->afp_wrap_readdir(gAfpConnection.GetVolume(), strDirName.c_str(), &dirEnt))
167 for (curDirPtr = dirEnt; curDirPtr; curDirPtr = curDirPtr->next)
170 aDir.type = curDirPtr->isdir;
172 aDir.name = curDirPtr->basic.name;
174 aDir.name = curDirPtr->name;
176 vecEntries.push_back(aDir);
178 gAfpConnection.GetImpl()->afp_ml_filebase_free(&dirEnt);
181 for (size_t i = 0; i < vecEntries.size(); i++)
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);
190 if (!strFile.Equals(".") && !strFile.Equals("..") && !strFile.Equals("lost+found"))
193 bool bIsDir = aDir.type;
194 int64_t lTimeDate = 0;
196 // if we not only list volumes - stat the files in folder
199 struct stat info = {0};
201 if (m_extFileInfo && g_advancedSettings.m_sambastatfiles)
203 // make sure we use the authenticated path wich contains any default username
204 CStdString strFullName = strDirName + strFile;
208 if (gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), strFullName.c_str(), &info) == 0)
211 if(S_ISLNK(info.st_mode))
214 if(!ResolveSymlink(strDirName, strFile, &info, linkUrl))
219 path = linkUrl.Get();
220 bIsDir = info.st_mode & S_IFDIR;
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;
229 CLog::Log(LOGERROR, "%s - Failed to stat file %s (%s)", __FUNCTION__, strFullName.c_str(),strerror(errno));
234 LONGLONG ll = Int32x32To64(lTimeDate & 0xffffffff, 10000000) + 116444736000000000ll;
235 fileTime.dwLowDateTime = (DWORD)(ll & 0xffffffff);
236 fileTime.dwHighDateTime = (DWORD)(ll >> 32);
237 FileTimeToLocalFileTime(&fileTime, &localTime);
242 localTime.dwHighDateTime = 0;
243 localTime.dwLowDateTime = 0;
246 CFileItemPtr pItem(new CFileItem(strFile));
247 pItem->m_dateTime = localTime;
248 pItem->m_dwSize = iSize;
252 URIUtils::AddSlashAtEnd(path);
253 pItem->m_bIsFolder = true;
257 pItem->m_bIsFolder = false;
260 if (!aDir.name.empty() && aDir.name[0] == '.')
262 pItem->SetProperty("file:hidden", true);
265 pItem->SetPath(path);
273 bool CAFPDirectory::Create(const char* strPath)
275 CSingleLock lock(gAfpConnection);
279 if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
282 CStdString strFilename = gAfpConnection.GetPath(url);
284 int result = gAfpConnection.GetImpl()->afp_wrap_mkdir(gAfpConnection.GetVolume(), strFilename.c_str(), 0);
287 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
289 return (result == 0 || EEXIST == result);
292 bool CAFPDirectory::Remove(const char *strPath)
294 CSingleLock lock(gAfpConnection);
297 if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
300 CStdString strFileName = gAfpConnection.GetPath(url);
302 int result = gAfpConnection.GetImpl()->afp_wrap_rmdir(gAfpConnection.GetVolume(), strFileName.c_str());
304 if (result != 0 && errno != ENOENT)
306 CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
313 bool CAFPDirectory::Exists(const char *strPath)
315 CSingleLock lock(gAfpConnection);
318 if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
321 CStdString strFileName(gAfpConnection.GetPath(url));
324 if (gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), strFileName.c_str(), &info) != 0)
327 return (info.st_mode & S_IFDIR) ? true : false;