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"
14 #include "network/ZeroconfBrowser.h"
15 #include "utils/URIUtils.h"
16 #include "utils/log.h"
21 using namespace XFILE
;
23 CZeroconfDirectory::CZeroconfDirectory()
25 CZeroconfBrowser::GetInstance()->Start();
28 CZeroconfDirectory::~CZeroconfDirectory() = default;
32 std::string
GetHumanReadableProtocol(std::string
const& fcr_service_type
)
34 if(fcr_service_type
== "_smb._tcp.")
36 else if(fcr_service_type
== "_ftp._tcp.")
38 else if(fcr_service_type
== "_webdav._tcp.")
40 else if(fcr_service_type
== "_nfs._tcp.")
42 else if(fcr_service_type
== "_sftp-ssh._tcp.")
44 //fallback, just return the received type
45 return fcr_service_type
;
47 bool GetXBMCProtocol(std::string
const& fcr_service_type
, std::string
& fr_protocol
)
49 if(fcr_service_type
== "_smb._tcp.")
51 else if(fcr_service_type
== "_ftp._tcp.")
53 else if(fcr_service_type
== "_webdav._tcp.")
55 else if(fcr_service_type
== "_nfs._tcp.")
57 else if(fcr_service_type
== "_sftp-ssh._tcp.")
65 bool GetDirectoryFromTxtRecords(const CZeroconfBrowser::ZeroconfService
& zeroconf_service
, CURL
& url
, CFileItemList
&items
)
69 //get the txt-records from this service
70 CZeroconfBrowser::ZeroconfService::tTxtRecordMap txtRecords
=zeroconf_service
.GetTxtRecords();
72 //if we have some records
73 if(!txtRecords
.empty())
79 //search for a path key entry
80 CZeroconfBrowser::ZeroconfService::tTxtRecordMap::iterator it
= txtRecords
.find(TXT_RECORD_PATH_KEY
);
82 //if we found the key - be sure there is a value there
83 if( it
!= txtRecords
.end() && !it
->second
.empty() )
85 //from now on we treat the value as a path - everything else would mean
86 //a misconfigured zeroconf server.
90 //search for a username key entry
91 it
= txtRecords
.find(TXT_RECORD_USERNAME_KEY
);
93 //if we found the key - be sure there is a value there
94 if( it
!= txtRecords
.end() && !it
->second
.empty() )
97 url
.SetUserName(username
);
100 //search for a password key entry
101 it
= txtRecords
.find(TXT_RECORD_PASSWORD_KEY
);
103 //if we found the key - be sure there is a value there
104 if( it
!= txtRecords
.end() && !it
->second
.empty() )
107 url
.SetPassword(password
);
110 //if we got a path - add a item - else at least we maybe have set username and password to theurl
113 CFileItemPtr
item(new CFileItem("", true));
114 std::string
urlStr(url
.Get());
115 //if path has a leading slash (sure it should have one)
116 if( path
.at(0) == '/' )
118 URIUtils::RemoveSlashAtEnd(urlStr
);//we don't need the slash at and of url then
120 else//path doesn't start with slash -
121 {//this is some kind of misconfiguration - we fix it by adding a slash to the url
122 URIUtils::AddSlashAtEnd(urlStr
);
125 //add slash at end of path since it has to be a folder
126 URIUtils::AddSlashAtEnd(path
);
127 //this is the full path including remote stuff (e.x. nfs://ip/path
128 item
->SetPath(urlStr
+ path
);
129 //remove the slash at the end of the path or GetFileName will not give the last dir
130 URIUtils::RemoveSlashAtEnd(path
);
131 //set the label to the last directory in path
132 if( !URIUtils::GetFileName(path
).empty() )
133 item
->SetLabel(URIUtils::GetFileName(path
));
137 item
->SetLabelPreformatted(true);
138 //just set the default folder icon
139 item
->FillInDefaultIcon();
140 item
->m_bIsShareOrDrive
=true;
148 bool CZeroconfDirectory::GetDirectory(const CURL
& url
, CFileItemList
&items
)
150 assert(url
.IsProtocol("zeroconf"));
151 std::string strPath
= url
.Get();
152 std::string path
= strPath
.substr(11, strPath
.length());
153 URIUtils::RemoveSlashAtEnd(path
);
156 std::vector
<CZeroconfBrowser::ZeroconfService
> found_services
= CZeroconfBrowser::GetInstance()->GetFoundServices();
157 for (auto& it
: found_services
)
159 //only use discovered services we can connect to through directory
161 if (GetXBMCProtocol(it
.GetType(), tmp
))
163 CFileItemPtr
item(new CFileItem("", true));
165 url
.SetProtocol("zeroconf");
166 std::string
service_path(CURL::Encode(CZeroconfBrowser::ZeroconfService::toPath(it
)));
167 url
.SetFileName(service_path
);
168 item
->SetPath(url
.Get());
170 //now do the formatting
171 std::string protocol
= GetHumanReadableProtocol(it
.GetType());
172 item
->SetLabel(it
.GetName() + " (" + protocol
+ ")");
173 item
->SetLabelPreformatted(true);
174 //just set the default folder icon
175 item
->FillInDefaultIcon();
183 //decode the path first
184 std::string
decoded(CURL::Decode(path
));
187 CZeroconfBrowser::ZeroconfService zeroconf_service
= CZeroconfBrowser::ZeroconfService::fromPath(decoded
);
189 if(!CZeroconfBrowser::GetInstance()->ResolveService(zeroconf_service
))
192 "CZeroconfDirectory::GetDirectory service ( {} ) could not be resolved in time",
193 zeroconf_service
.GetName());
198 assert(!zeroconf_service
.GetIP().empty());
200 service
.SetPort(zeroconf_service
.GetPort());
201 service
.SetHostName(zeroconf_service
.GetIP());
202 //do protocol conversion (_smb._tcp -> smb)
203 //! @todo try automatic conversion -> remove leading '_' and '._tcp'?
204 std::string protocol
;
205 if(!GetXBMCProtocol(zeroconf_service
.GetType(), protocol
))
208 "CZeroconfDirectory::GetDirectory Unknown service type ({}), skipping; ",
209 zeroconf_service
.GetType());
213 service
.SetProtocol(protocol
);
215 //first try to show the txt-record defined path if any
216 if(GetDirectoryFromTxtRecords(zeroconf_service
, service
, items
))
220 else//no txt record path - so let the CDirectory handler show the folders
222 return CDirectory::GetDirectory(service
.Get(), items
, "", DIR_FLAG_ALLOW_PROMPT
);
225 } catch (std::runtime_error
& e
) {
227 "CZeroconfDirectory::GetDirectory failed getting directory: '{}'. Error: '{}'",