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.
9 #include "ZeroconfDirectory.h"
11 #include "Directory.h"
13 #include "FileItemList.h"
15 #include "network/ZeroconfBrowser.h"
16 #include "utils/URIUtils.h"
17 #include "utils/log.h"
22 using namespace XFILE
;
24 CZeroconfDirectory::CZeroconfDirectory()
26 CZeroconfBrowser::GetInstance()->Start();
29 CZeroconfDirectory::~CZeroconfDirectory() = default;
33 std::string
GetHumanReadableProtocol(std::string
const& fcr_service_type
)
35 if(fcr_service_type
== "_smb._tcp.")
37 else if(fcr_service_type
== "_ftp._tcp.")
39 else if(fcr_service_type
== "_webdav._tcp.")
41 else if(fcr_service_type
== "_nfs._tcp.")
43 else if(fcr_service_type
== "_sftp-ssh._tcp.")
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.")
52 else if(fcr_service_type
== "_ftp._tcp.")
54 else if(fcr_service_type
== "_webdav._tcp.")
56 else if(fcr_service_type
== "_nfs._tcp.")
58 else if(fcr_service_type
== "_sftp-ssh._tcp.")
66 bool GetDirectoryFromTxtRecords(const CZeroconfBrowser::ZeroconfService
& zeroconf_service
, CURL
& url
, CFileItemList
&items
)
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())
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.
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() )
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() )
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
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
));
138 item
->SetLabelPreformatted(true);
139 //just set the default folder icon
140 item
->FillInDefaultIcon();
141 item
->m_bIsShareOrDrive
=true;
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
);
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
162 if (GetXBMCProtocol(it
.GetType(), tmp
))
164 CFileItemPtr
item(new CFileItem("", true));
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();
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
))
193 "CZeroconfDirectory::GetDirectory service ( {} ) could not be resolved in time",
194 zeroconf_service
.GetName());
199 assert(!zeroconf_service
.GetIP().empty());
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
))
209 "CZeroconfDirectory::GetDirectory Unknown service type ({}), skipping; ",
210 zeroconf_service
.GetType());
214 service
.SetProtocol(protocol
);
216 //first try to show the txt-record defined path if any
217 if(GetDirectoryFromTxtRecords(zeroconf_service
, service
, items
))
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
) {
228 "CZeroconfDirectory::GetDirectory failed getting directory: '{}'. Error: '{}'",