2 * Copyright (C) 2005-2013 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, see
17 * <http://www.gnu.org/licenses/>.
21 #include "ZeroconfDirectory.h"
25 #include "utils/URIUtils.h"
27 #include "network/ZeroconfBrowser.h"
28 #include "Directory.h"
29 #include "utils/log.h"
31 using namespace XFILE;
33 CZeroconfDirectory::CZeroconfDirectory()
35 CZeroconfBrowser::GetInstance()->Start();
38 CZeroconfDirectory::~CZeroconfDirectory()
44 CStdString GetHumanReadableProtocol(std::string const& fcr_service_type)
46 if(fcr_service_type == "_smb._tcp.")
48 else if(fcr_service_type == "_ftp._tcp.")
50 else if(fcr_service_type == "_htsp._tcp.")
52 else if(fcr_service_type == "_daap._tcp.")
53 return "iTunes Music Sharing";
54 else if(fcr_service_type == "_webdav._tcp.")
56 else if(fcr_service_type == "_nfs._tcp.")
58 else if(fcr_service_type == "_afpovertcp._tcp.")
60 else if(fcr_service_type == "_sftp-ssh._tcp.")
62 //fallback, just return the received type
63 return fcr_service_type;
65 bool GetXBMCProtocol(std::string const& fcr_service_type, CStdString& fr_protocol)
67 if(fcr_service_type == "_smb._tcp.")
69 else if(fcr_service_type == "_ftp._tcp.")
71 else if(fcr_service_type == "_htsp._tcp.")
73 else if(fcr_service_type == "_daap._tcp.")
75 else if(fcr_service_type == "_webdav._tcp.")
77 else if(fcr_service_type == "_nfs._tcp.")
79 else if(fcr_service_type == "_afpovertcp._tcp.")
81 else if(fcr_service_type == "_sftp-ssh._tcp.")
89 bool GetDirectoryFromTxtRecords(CZeroconfBrowser::ZeroconfService zeroconf_service, CURL& url, CFileItemList &items)
93 //get the txt-records from this service
94 CZeroconfBrowser::ZeroconfService::tTxtRecordMap txtRecords=zeroconf_service.GetTxtRecords();
96 //if we have some records
97 if(!txtRecords.empty())
103 //search for a path key entry
104 CZeroconfBrowser::ZeroconfService::tTxtRecordMap::iterator it = txtRecords.find(TXT_RECORD_PATH_KEY);
106 //if we found the key - be sure there is a value there
107 if( it != txtRecords.end() && !it->second.empty() )
109 //from now on we treat the value as a path - everything else would mean
110 //a missconfigured zeroconf server.
114 //search for a username key entry
115 it = txtRecords.find(TXT_RECORD_USERNAME_KEY);
117 //if we found the key - be sure there is a value there
118 if( it != txtRecords.end() && !it->second.empty() )
121 url.SetUserName(username);
124 //search for a password key entry
125 it = txtRecords.find(TXT_RECORD_PASSWORD_KEY);
127 //if we found the key - be sure there is a value there
128 if( it != txtRecords.end() && !it->second.empty() )
131 url.SetPassword(password);
134 //if we got a path - add a item - else at least we maybe have set username and password to theurl
137 CFileItemPtr item(new CFileItem("", true));
138 CStdString urlStr(url.Get());
139 //if path has a leading slash (sure it should have one)
140 if( path.at(0) == '/' )
142 URIUtils::RemoveSlashAtEnd(urlStr);//we don't need the slash at and of url then
144 else//path doesn't start with slash -
145 {//this is some kind of missconfiguration - we fix it by adding a slash to the url
146 URIUtils::AddSlashAtEnd(urlStr);
149 //add slash at end of path since it has to be a folder
150 URIUtils::AddSlashAtEnd(path);
151 //this is the full path includeing remote stuff (e.x. nfs://ip/path
152 item->SetPath(urlStr + path);
153 //remove the slash at the end of the path or GetFileName will not give the last dir
154 URIUtils::RemoveSlashAtEnd(path);
155 //set the label to the last directory in path
156 if( !URIUtils::GetFileName(path).empty() )
157 item->SetLabel(URIUtils::GetFileName(path));
161 item->SetLabelPreformated(true);
162 //just set the default folder icon
163 item->FillInDefaultIcon();
164 item->m_bIsShareOrDrive=true;
172 bool CZeroconfDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
174 assert(strPath.substr(0, 11) == "zeroconf://");
175 CStdString path = strPath.substr(11, strPath.length());
176 URIUtils::RemoveSlashAtEnd(path);
179 std::vector<CZeroconfBrowser::ZeroconfService> found_services = CZeroconfBrowser::GetInstance()->GetFoundServices();
180 for(std::vector<CZeroconfBrowser::ZeroconfService>::iterator it = found_services.begin(); it != found_services.end(); ++it)
182 //only use discovered services we can connect to through directory
184 if(GetXBMCProtocol(it->GetType(), tmp))
186 CFileItemPtr item(new CFileItem("", true));
188 url.SetProtocol("zeroconf");
189 CStdString service_path = CZeroconfBrowser::ZeroconfService::toPath(*it);
190 CURL::Encode(service_path);
191 url.SetFileName(service_path);
192 item->SetPath(url.Get());
194 //now do the formatting
195 CStdString protocol = GetHumanReadableProtocol(it->GetType());
196 item->SetLabel(it->GetName() + " (" + protocol + ")");
197 item->SetLabelPreformated(true);
198 //just set the default folder icon
199 item->FillInDefaultIcon();
207 //decode the path first
208 CStdString decoded = path;
209 CURL::Decode(decoded);
212 CZeroconfBrowser::ZeroconfService zeroconf_service = CZeroconfBrowser::ZeroconfService::fromPath(decoded);
214 if(!CZeroconfBrowser::GetInstance()->ResolveService(zeroconf_service))
216 CLog::Log(LOGINFO, "CZeroconfDirectory::GetDirectory service ( %s ) could not be resolved in time", zeroconf_service.GetName().c_str());
221 assert(!zeroconf_service.GetIP().empty());
223 service.SetPort(zeroconf_service.GetPort());
224 service.SetHostName(zeroconf_service.GetIP());
225 //do protocol conversion (_smb._tcp -> smb)
226 //ToDo: try automatic conversion -> remove leading '_' and '._tcp'?
228 if(!GetXBMCProtocol(zeroconf_service.GetType(), protocol))
230 CLog::Log(LOGERROR, "CZeroconfDirectory::GetDirectory Unknown service type (%s), skipping; ", zeroconf_service.GetType().c_str());
234 service.SetProtocol(protocol);
236 //first try to show the txt-record defined path if any
237 if(GetDirectoryFromTxtRecords(zeroconf_service, service, items))
241 else//no txt record path - so let the CDirectory handler show the folders
243 return CDirectory::GetDirectory(service.Get(), items, "", DIR_FLAG_ALLOW_PROMPT);
246 } catch (std::runtime_error& e) {
247 CLog::Log(LOGERROR, "CZeroconfDirectory::GetDirectory failed getting directory: '%s'. Error: '%s'", decoded.c_str(), e.what());