2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
20 #include "system.h" //HAS_ZEROCONF define
21 #include "ZeroconfBrowser.h"
23 #include "utils/log.h"
25 #if defined (HAS_AVAHI)
26 #include "linux/ZeroconfBrowserAvahi.h"
27 #elif defined(TARGET_DARWIN)
28 //on osx use the native implementation
29 #include "osx/ZeroconfBrowserOSX.h"
30 #elif defined(HAS_MDNS)
31 #include "mdns/ZeroconfBrowserMDNS.h"
34 #include "threads/CriticalSection.h"
35 #include "threads/SingleLock.h"
36 #include "threads/Atomics.h"
38 #if !defined(HAS_ZEROCONF)
39 //dummy implementation used if no zeroconf is present
40 //should be optimized away
41 class CZeroconfBrowserDummy : public CZeroconfBrowser
43 virtual bool doAddServiceType(const CStdString&){return false;}
44 virtual bool doRemoveServiceType(const CStdString&){return false;}
45 virtual std::vector<ZeroconfService> doGetFoundServices(){return std::vector<ZeroconfService>();}
46 virtual bool doResolveService(ZeroconfService&, double){return false;}
50 long CZeroconfBrowser::sm_singleton_guard = 0;
51 CZeroconfBrowser* CZeroconfBrowser::smp_instance = 0;
53 CZeroconfBrowser::CZeroconfBrowser():mp_crit_sec(new CCriticalSection),m_started(false)
55 #ifdef HAS_FILESYSTEM_SMB
56 AddServiceType("_smb._tcp.");
58 AddServiceType("_ftp._tcp.");
59 AddServiceType("_htsp._tcp.");
60 AddServiceType("_daap._tcp.");
61 AddServiceType("_webdav._tcp.");
62 #ifdef HAS_FILESYSTEM_NFS
63 AddServiceType("_nfs._tcp.");
64 #endif// HAS_FILESYSTEM_NFS
65 #ifdef HAS_FILESYSTEM_AFP
66 AddServiceType("_afpovertcp._tcp.");
68 AddServiceType("_sftp-ssh._tcp.");
71 CZeroconfBrowser::~CZeroconfBrowser()
76 void CZeroconfBrowser::Start()
78 CSingleLock lock(*mp_crit_sec);
82 for(tServices::const_iterator it = m_services.begin(); it != m_services.end(); ++it)
83 doAddServiceType(*it);
86 void CZeroconfBrowser::Stop()
88 CSingleLock lock(*mp_crit_sec);
91 for(tServices::iterator it = m_services.begin(); it != m_services.end(); ++it)
92 RemoveServiceType(*it);
96 bool CZeroconfBrowser::AddServiceType(const CStdString& fcr_service_type /*const CStdString& domain*/ )
98 CSingleLock lock(*mp_crit_sec);
99 std::pair<tServices::iterator, bool> ret = m_services.insert(fcr_service_type);
102 //service already in list
106 return doAddServiceType(*ret.first);
107 //not yet started, so its just queued
111 bool CZeroconfBrowser::RemoveServiceType(const CStdString& fcr_service_type)
113 CSingleLock lock(*mp_crit_sec);
114 tServices::iterator ret = m_services.find(fcr_service_type);
115 if(ret == m_services.end())
118 return doRemoveServiceType(fcr_service_type);
119 //not yet started, so its just queued
123 std::vector<CZeroconfBrowser::ZeroconfService> CZeroconfBrowser::GetFoundServices()
125 CSingleLock lock(*mp_crit_sec);
127 return doGetFoundServices();
130 CLog::Log(LOGDEBUG, "CZeroconfBrowser::GetFoundServices asked for services without browser running");
131 return std::vector<ZeroconfService>();
135 bool CZeroconfBrowser::ResolveService(ZeroconfService& fr_service, double f_timeout)
137 CSingleLock lock(*mp_crit_sec);
140 return doResolveService(fr_service, f_timeout);
142 CLog::Log(LOGDEBUG, "CZeroconfBrowser::GetFoundServices asked for services without browser running");
146 CZeroconfBrowser* CZeroconfBrowser::GetInstance()
150 //use double checked locking
151 CAtomicSpinLock lock(sm_singleton_guard);
154 #if !defined(HAS_ZEROCONF)
155 smp_instance = new CZeroconfBrowserDummy;
157 #if defined(TARGET_DARWIN)
158 smp_instance = new CZeroconfBrowserOSX;
159 #elif defined(HAS_AVAHI)
160 smp_instance = new CZeroconfBrowserAvahi;
161 #elif defined(HAS_MDNS)
162 smp_instance = new CZeroconfBrowserMDNS;
167 assert(smp_instance);
171 void CZeroconfBrowser::ReleaseInstance()
173 CAtomicSpinLock lock(sm_singleton_guard);
179 CZeroconfBrowser::ZeroconfService::ZeroconfService():m_port(0){}
181 CZeroconfBrowser::ZeroconfService::ZeroconfService(const CStdString& fcr_name, const CStdString& fcr_type, const CStdString& fcr_domain):
183 m_domain(fcr_domain),
188 void CZeroconfBrowser::ZeroconfService::SetName(const CStdString& fcr_name)
193 void CZeroconfBrowser::ZeroconfService::SetType(const CStdString& fcr_type)
196 throw std::runtime_error("CZeroconfBrowser::ZeroconfService::SetType invalid type: "+ fcr_type);
197 //make sure there's a "." as last char (differs for avahi and osx implementation of browsers)
198 if(fcr_type[fcr_type.length() - 1] != '.')
199 m_type = fcr_type + ".";
204 void CZeroconfBrowser::ZeroconfService::SetDomain(const CStdString& fcr_domain)
206 m_domain = fcr_domain;
209 void CZeroconfBrowser::ZeroconfService::SetHostname(const CStdString& fcr_hostname)
211 m_hostname = fcr_hostname;
214 void CZeroconfBrowser::ZeroconfService::SetIP(const CStdString& fcr_ip)
219 void CZeroconfBrowser::ZeroconfService::SetPort(int f_port)
224 void CZeroconfBrowser::ZeroconfService::SetTxtRecords(const tTxtRecordMap& txt_records)
226 m_txtrecords_map = txt_records;
228 CLog::Log(LOGDEBUG,"CZeroconfBrowser: dump txt-records");
229 for(tTxtRecordMap::const_iterator it = m_txtrecords_map.begin(); it != m_txtrecords_map.end(); ++it)
231 CLog::Log(LOGDEBUG,"CZeroconfBrowser: key: %s value: %s",it->first.c_str(), it->second.c_str());
235 CStdString CZeroconfBrowser::ZeroconfService::toPath(const ZeroconfService& fcr_service)
237 return CStdString(fcr_service.m_type + "@" + fcr_service.m_domain + "@" + fcr_service.m_name);
240 CZeroconfBrowser::ZeroconfService CZeroconfBrowser::ZeroconfService::fromPath(const CStdString& fcr_path)
242 if( fcr_path.empty() )
243 throw std::runtime_error("CZeroconfBrowser::ZeroconfService::fromPath input string empty!");
245 int pos1 = fcr_path.Find('@'); //first @
246 int pos2 = fcr_path.Find('@', pos1+1); //second
248 if( pos1 == -1 || pos2 == -1 )
249 throw std::runtime_error("CZeroconfBrowser::ZeroconfService::fromPath invalid input path");
251 return ZeroconfService(
252 fcr_path.substr(pos2 + 1, fcr_path.length()), //name
253 fcr_path.substr(0, pos1), //type
254 fcr_path.substr(pos1 + 1, pos2-(pos1+1)) //domain