[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / filesystem / ZeroconfDirectory.cpp
blobf34b6b2f1f27b212ba32fc997b78efe6595478ce
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 "URL.h"
14 #include "network/ZeroconfBrowser.h"
15 #include "utils/URIUtils.h"
16 #include "utils/log.h"
18 #include <cassert>
19 #include <stdexcept>
21 using namespace XFILE;
23 CZeroconfDirectory::CZeroconfDirectory()
25 CZeroconfBrowser::GetInstance()->Start();
28 CZeroconfDirectory::~CZeroconfDirectory() = default;
30 namespace
32 std::string GetHumanReadableProtocol(std::string const& fcr_service_type)
34 if(fcr_service_type == "_smb._tcp.")
35 return "SAMBA";
36 else if(fcr_service_type == "_ftp._tcp.")
37 return "FTP";
38 else if(fcr_service_type == "_webdav._tcp.")
39 return "WebDAV";
40 else if(fcr_service_type == "_nfs._tcp.")
41 return "NFS";
42 else if(fcr_service_type == "_sftp-ssh._tcp.")
43 return "SFTP";
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.")
50 fr_protocol = "smb";
51 else if(fcr_service_type == "_ftp._tcp.")
52 fr_protocol = "ftp";
53 else if(fcr_service_type == "_webdav._tcp.")
54 fr_protocol = "dav";
55 else if(fcr_service_type == "_nfs._tcp.")
56 fr_protocol = "nfs";
57 else if(fcr_service_type == "_sftp-ssh._tcp.")
58 fr_protocol = "sftp";
59 else
60 return false;
61 return true;
65 bool GetDirectoryFromTxtRecords(const CZeroconfBrowser::ZeroconfService& zeroconf_service, CURL& url, CFileItemList &items)
67 bool ret = false;
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())
75 std::string path;
76 std::string username;
77 std::string password;
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.
87 path=it->second;
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() )
96 username=it->second;
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() )
106 password=it->second;
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
111 if( !path.empty())
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));
134 else
135 item->SetLabel("/");
137 item->SetLabelPreformatted(true);
138 //just set the default folder icon
139 item->FillInDefaultIcon();
140 item->m_bIsShareOrDrive=true;
141 items.Add(item);
142 ret = true;
145 return ret;
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);
154 if(path.empty())
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
160 std::string tmp;
161 if (GetXBMCProtocol(it.GetType(), tmp))
163 CFileItemPtr item(new CFileItem("", true));
164 CURL url;
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();
176 items.Add(item);
179 return true;
181 else
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))
191 CLog::Log(LOGINFO,
192 "CZeroconfDirectory::GetDirectory service ( {} ) could not be resolved in time",
193 zeroconf_service.GetName());
194 return false;
196 else
198 assert(!zeroconf_service.GetIP().empty());
199 CURL service;
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))
207 CLog::Log(LOGERROR,
208 "CZeroconfDirectory::GetDirectory Unknown service type ({}), skipping; ",
209 zeroconf_service.GetType());
210 return false;
213 service.SetProtocol(protocol);
215 //first try to show the txt-record defined path if any
216 if(GetDirectoryFromTxtRecords(zeroconf_service, service, items))
218 return true;
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) {
226 CLog::Log(LOGERROR,
227 "CZeroconfDirectory::GetDirectory failed getting directory: '{}'. Error: '{}'",
228 decoded, e.what());
229 return false;