diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/filesystem/ZeroconfDirectory.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/filesystem/ZeroconfDirectory.cpp')
-rw-r--r-- | xbmc/filesystem/ZeroconfDirectory.cpp | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/xbmc/filesystem/ZeroconfDirectory.cpp b/xbmc/filesystem/ZeroconfDirectory.cpp new file mode 100644 index 0000000..f34b6b2 --- /dev/null +++ b/xbmc/filesystem/ZeroconfDirectory.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ZeroconfDirectory.h" + +#include "Directory.h" +#include "FileItem.h" +#include "URL.h" +#include "network/ZeroconfBrowser.h" +#include "utils/URIUtils.h" +#include "utils/log.h" + +#include <cassert> +#include <stdexcept> + +using namespace XFILE; + +CZeroconfDirectory::CZeroconfDirectory() +{ + CZeroconfBrowser::GetInstance()->Start(); +} + +CZeroconfDirectory::~CZeroconfDirectory() = default; + +namespace +{ + std::string GetHumanReadableProtocol(std::string const& fcr_service_type) + { + if(fcr_service_type == "_smb._tcp.") + return "SAMBA"; + else if(fcr_service_type == "_ftp._tcp.") + return "FTP"; + else if(fcr_service_type == "_webdav._tcp.") + return "WebDAV"; + else if(fcr_service_type == "_nfs._tcp.") + return "NFS"; + else if(fcr_service_type == "_sftp-ssh._tcp.") + return "SFTP"; + //fallback, just return the received type + return fcr_service_type; + } + bool GetXBMCProtocol(std::string const& fcr_service_type, std::string& fr_protocol) + { + if(fcr_service_type == "_smb._tcp.") + fr_protocol = "smb"; + else if(fcr_service_type == "_ftp._tcp.") + fr_protocol = "ftp"; + else if(fcr_service_type == "_webdav._tcp.") + fr_protocol = "dav"; + else if(fcr_service_type == "_nfs._tcp.") + fr_protocol = "nfs"; + else if(fcr_service_type == "_sftp-ssh._tcp.") + fr_protocol = "sftp"; + else + return false; + return true; + } +} + +bool GetDirectoryFromTxtRecords(const CZeroconfBrowser::ZeroconfService& zeroconf_service, CURL& url, CFileItemList &items) +{ + bool ret = false; + + //get the txt-records from this service + CZeroconfBrowser::ZeroconfService::tTxtRecordMap txtRecords=zeroconf_service.GetTxtRecords(); + + //if we have some records + if(!txtRecords.empty()) + { + std::string path; + std::string username; + std::string password; + + //search for a path key entry + CZeroconfBrowser::ZeroconfService::tTxtRecordMap::iterator it = txtRecords.find(TXT_RECORD_PATH_KEY); + + //if we found the key - be sure there is a value there + if( it != txtRecords.end() && !it->second.empty() ) + { + //from now on we treat the value as a path - everything else would mean + //a misconfigured zeroconf server. + path=it->second; + } + + //search for a username key entry + it = txtRecords.find(TXT_RECORD_USERNAME_KEY); + + //if we found the key - be sure there is a value there + if( it != txtRecords.end() && !it->second.empty() ) + { + username=it->second; + url.SetUserName(username); + } + + //search for a password key entry + it = txtRecords.find(TXT_RECORD_PASSWORD_KEY); + + //if we found the key - be sure there is a value there + if( it != txtRecords.end() && !it->second.empty() ) + { + password=it->second; + url.SetPassword(password); + } + + //if we got a path - add a item - else at least we maybe have set username and password to theurl + if( !path.empty()) + { + CFileItemPtr item(new CFileItem("", true)); + std::string urlStr(url.Get()); + //if path has a leading slash (sure it should have one) + if( path.at(0) == '/' ) + { + URIUtils::RemoveSlashAtEnd(urlStr);//we don't need the slash at and of url then + } + else//path doesn't start with slash - + {//this is some kind of misconfiguration - we fix it by adding a slash to the url + URIUtils::AddSlashAtEnd(urlStr); + } + + //add slash at end of path since it has to be a folder + URIUtils::AddSlashAtEnd(path); + //this is the full path including remote stuff (e.x. nfs://ip/path + item->SetPath(urlStr + path); + //remove the slash at the end of the path or GetFileName will not give the last dir + URIUtils::RemoveSlashAtEnd(path); + //set the label to the last directory in path + if( !URIUtils::GetFileName(path).empty() ) + item->SetLabel(URIUtils::GetFileName(path)); + else + item->SetLabel("/"); + + item->SetLabelPreformatted(true); + //just set the default folder icon + item->FillInDefaultIcon(); + item->m_bIsShareOrDrive=true; + items.Add(item); + ret = true; + } + } + return ret; +} + +bool CZeroconfDirectory::GetDirectory(const CURL& url, CFileItemList &items) +{ + assert(url.IsProtocol("zeroconf")); + std::string strPath = url.Get(); + std::string path = strPath.substr(11, strPath.length()); + URIUtils::RemoveSlashAtEnd(path); + if(path.empty()) + { + std::vector<CZeroconfBrowser::ZeroconfService> found_services = CZeroconfBrowser::GetInstance()->GetFoundServices(); + for (auto& it : found_services) + { + //only use discovered services we can connect to through directory + std::string tmp; + if (GetXBMCProtocol(it.GetType(), tmp)) + { + CFileItemPtr item(new CFileItem("", true)); + CURL url; + url.SetProtocol("zeroconf"); + std::string service_path(CURL::Encode(CZeroconfBrowser::ZeroconfService::toPath(it))); + url.SetFileName(service_path); + item->SetPath(url.Get()); + + //now do the formatting + std::string protocol = GetHumanReadableProtocol(it.GetType()); + item->SetLabel(it.GetName() + " (" + protocol + ")"); + item->SetLabelPreformatted(true); + //just set the default folder icon + item->FillInDefaultIcon(); + items.Add(item); + } + } + return true; + } + else + { + //decode the path first + std::string decoded(CURL::Decode(path)); + try + { + CZeroconfBrowser::ZeroconfService zeroconf_service = CZeroconfBrowser::ZeroconfService::fromPath(decoded); + + if(!CZeroconfBrowser::GetInstance()->ResolveService(zeroconf_service)) + { + CLog::Log(LOGINFO, + "CZeroconfDirectory::GetDirectory service ( {} ) could not be resolved in time", + zeroconf_service.GetName()); + return false; + } + else + { + assert(!zeroconf_service.GetIP().empty()); + CURL service; + service.SetPort(zeroconf_service.GetPort()); + service.SetHostName(zeroconf_service.GetIP()); + //do protocol conversion (_smb._tcp -> smb) + //! @todo try automatic conversion -> remove leading '_' and '._tcp'? + std::string protocol; + if(!GetXBMCProtocol(zeroconf_service.GetType(), protocol)) + { + CLog::Log(LOGERROR, + "CZeroconfDirectory::GetDirectory Unknown service type ({}), skipping; ", + zeroconf_service.GetType()); + return false; + } + + service.SetProtocol(protocol); + + //first try to show the txt-record defined path if any + if(GetDirectoryFromTxtRecords(zeroconf_service, service, items)) + { + return true; + } + else//no txt record path - so let the CDirectory handler show the folders + { + return CDirectory::GetDirectory(service.Get(), items, "", DIR_FLAG_ALLOW_PROMPT); + } + } + } catch (std::runtime_error& e) { + CLog::Log(LOGERROR, + "CZeroconfDirectory::GetDirectory failed getting directory: '{}'. Error: '{}'", + decoded, e.what()); + return false; + } + } +} |