[windows] Fix MAC Address Discovery
[xbmc.git] / xbmc / filesystem / ZeroconfDirectory.cpp
blobd05184e394bdf4ef550747dcb805292f43ea8926
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "ZeroconfDirectory.h"
11 #include "Directory.h"
12 #include "FileItem.h"
13 #include "FileItemList.h"
14 #include "URL.h"
15 #include "network/ZeroconfBrowser.h"
16 #include "utils/URIUtils.h"
17 #include "utils/log.h"
19 #include <cassert>
20 #include <stdexcept>
22 using namespace XFILE;
24 CZeroconfDirectory::CZeroconfDirectory()
26 CZeroconfBrowser::GetInstance()->Start();
29 CZeroconfDirectory::~CZeroconfDirectory() = default;
31 namespace
33 std::string GetHumanReadableProtocol(std::string const& fcr_service_type)
35 if(fcr_service_type == "_smb._tcp.")
36 return "SAMBA";
37 else if(fcr_service_type == "_ftp._tcp.")
38 return "FTP";
39 else if(fcr_service_type == "_webdav._tcp.")
40 return "WebDAV";
41 else if(fcr_service_type == "_nfs._tcp.")
42 return "NFS";
43 else if(fcr_service_type == "_sftp-ssh._tcp.")
44 return "SFTP";
45 //fallback, just return the received type
46 return fcr_service_type;
48 bool GetXBMCProtocol(std::string const& fcr_service_type, std::string& fr_protocol)
50 if(fcr_service_type == "_smb._tcp.")
51 fr_protocol = "smb";
52 else if(fcr_service_type == "_ftp._tcp.")
53 fr_protocol = "ftp";
54 else if(fcr_service_type == "_webdav._tcp.")
55 fr_protocol = "dav";
56 else if(fcr_service_type == "_nfs._tcp.")
57 fr_protocol = "nfs";
58 else if(fcr_service_type == "_sftp-ssh._tcp.")
59 fr_protocol = "sftp";
60 else
61 return false;
62 return true;
66 bool GetDirectoryFromTxtRecords(const CZeroconfBrowser::ZeroconfService& zeroconf_service, CURL& url, CFileItemList &items)
68 bool ret = false;
70 //get the txt-records from this service
71 CZeroconfBrowser::ZeroconfService::tTxtRecordMap txtRecords=zeroconf_service.GetTxtRecords();
73 //if we have some records
74 if(!txtRecords.empty())
76 std::string path;
77 std::string username;
78 std::string password;
80 //search for a path key entry
81 CZeroconfBrowser::ZeroconfService::tTxtRecordMap::iterator it = txtRecords.find(TXT_RECORD_PATH_KEY);
83 //if we found the key - be sure there is a value there
84 if( it != txtRecords.end() && !it->second.empty() )
86 //from now on we treat the value as a path - everything else would mean
87 //a misconfigured zeroconf server.
88 path=it->second;
91 //search for a username key entry
92 it = txtRecords.find(TXT_RECORD_USERNAME_KEY);
94 //if we found the key - be sure there is a value there
95 if( it != txtRecords.end() && !it->second.empty() )
97 username=it->second;
98 url.SetUserName(username);
101 //search for a password key entry
102 it = txtRecords.find(TXT_RECORD_PASSWORD_KEY);
104 //if we found the key - be sure there is a value there
105 if( it != txtRecords.end() && !it->second.empty() )
107 password=it->second;
108 url.SetPassword(password);
111 //if we got a path - add a item - else at least we maybe have set username and password to theurl
112 if( !path.empty())
114 CFileItemPtr item(new CFileItem("", true));
115 std::string urlStr(url.Get());
116 //if path has a leading slash (sure it should have one)
117 if( path.at(0) == '/' )
119 URIUtils::RemoveSlashAtEnd(urlStr);//we don't need the slash at and of url then
121 else//path doesn't start with slash -
122 {//this is some kind of misconfiguration - we fix it by adding a slash to the url
123 URIUtils::AddSlashAtEnd(urlStr);
126 //add slash at end of path since it has to be a folder
127 URIUtils::AddSlashAtEnd(path);
128 //this is the full path including remote stuff (e.x. nfs://ip/path
129 item->SetPath(urlStr + path);
130 //remove the slash at the end of the path or GetFileName will not give the last dir
131 URIUtils::RemoveSlashAtEnd(path);
132 //set the label to the last directory in path
133 if( !URIUtils::GetFileName(path).empty() )
134 item->SetLabel(URIUtils::GetFileName(path));
135 else
136 item->SetLabel("/");
138 item->SetLabelPreformatted(true);
139 //just set the default folder icon
140 item->FillInDefaultIcon();
141 item->m_bIsShareOrDrive=true;
142 items.Add(item);
143 ret = true;
146 return ret;
149 bool CZeroconfDirectory::GetDirectory(const CURL& url, CFileItemList &items)
151 assert(url.IsProtocol("zeroconf"));
152 std::string strPath = url.Get();
153 std::string path = strPath.substr(11, strPath.length());
154 URIUtils::RemoveSlashAtEnd(path);
155 if(path.empty())
157 std::vector<CZeroconfBrowser::ZeroconfService> found_services = CZeroconfBrowser::GetInstance()->GetFoundServices();
158 for (auto& it : found_services)
160 //only use discovered services we can connect to through directory
161 std::string tmp;
162 if (GetXBMCProtocol(it.GetType(), tmp))
164 CFileItemPtr item(new CFileItem("", true));
165 CURL url;
166 url.SetProtocol("zeroconf");
167 std::string service_path(CURL::Encode(CZeroconfBrowser::ZeroconfService::toPath(it)));
168 url.SetFileName(service_path);
169 item->SetPath(url.Get());
171 //now do the formatting
172 std::string protocol = GetHumanReadableProtocol(it.GetType());
173 item->SetLabel(it.GetName() + " (" + protocol + ")");
174 item->SetLabelPreformatted(true);
175 //just set the default folder icon
176 item->FillInDefaultIcon();
177 items.Add(item);
180 return true;
182 else
184 //decode the path first
185 std::string decoded(CURL::Decode(path));
188 CZeroconfBrowser::ZeroconfService zeroconf_service = CZeroconfBrowser::ZeroconfService::fromPath(decoded);
190 if(!CZeroconfBrowser::GetInstance()->ResolveService(zeroconf_service))
192 CLog::Log(LOGINFO,
193 "CZeroconfDirectory::GetDirectory service ( {} ) could not be resolved in time",
194 zeroconf_service.GetName());
195 return false;
197 else
199 assert(!zeroconf_service.GetIP().empty());
200 CURL service;
201 service.SetPort(zeroconf_service.GetPort());
202 service.SetHostName(zeroconf_service.GetIP());
203 //do protocol conversion (_smb._tcp -> smb)
204 //! @todo try automatic conversion -> remove leading '_' and '._tcp'?
205 std::string protocol;
206 if(!GetXBMCProtocol(zeroconf_service.GetType(), protocol))
208 CLog::Log(LOGERROR,
209 "CZeroconfDirectory::GetDirectory Unknown service type ({}), skipping; ",
210 zeroconf_service.GetType());
211 return false;
214 service.SetProtocol(protocol);
216 //first try to show the txt-record defined path if any
217 if(GetDirectoryFromTxtRecords(zeroconf_service, service, items))
219 return true;
221 else//no txt record path - so let the CDirectory handler show the folders
223 return CDirectory::GetDirectory(service.Get(), items, "", DIR_FLAG_ALLOW_PROMPT);
226 } catch (std::runtime_error& e) {
227 CLog::Log(LOGERROR,
228 "CZeroconfDirectory::GetDirectory failed getting directory: '{}'. Error: '{}'",
229 decoded, e.what());
230 return false;