[mdns] - enable zeroconfbrowser via mdnsembedded - as the browsing/service-resolveing...
[vuplus_xbmc] / xbmc / network / ZeroconfBrowser.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20 #include "system.h" //HAS_ZEROCONF define
21 #include "ZeroconfBrowser.h"
22 #include <stdexcept>
23 #include "utils/log.h"
24
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"
32 #endif
33
34 #include "threads/CriticalSection.h"
35 #include "threads/SingleLock.h"
36 #include "threads/Atomics.h"
37
38 #if !defined(HAS_ZEROCONF)
39 //dummy implementation used if no zeroconf is present
40 //should be optimized away
41 class CZeroconfBrowserDummy : public CZeroconfBrowser
42 {
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;}
47 };
48 #endif
49
50 long CZeroconfBrowser::sm_singleton_guard = 0;
51 CZeroconfBrowser* CZeroconfBrowser::smp_instance = 0;
52
53 CZeroconfBrowser::CZeroconfBrowser():mp_crit_sec(new CCriticalSection),m_started(false)
54 {
55 #ifdef HAS_FILESYSTEM_SMB
56   AddServiceType("_smb._tcp.");
57 #endif
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.");   
67 #endif
68   AddServiceType("_sftp-ssh._tcp.");
69 }
70
71 CZeroconfBrowser::~CZeroconfBrowser()
72 {
73   delete mp_crit_sec;
74 }
75
76 void CZeroconfBrowser::Start()
77 {
78   CSingleLock lock(*mp_crit_sec);
79   if(m_started)
80     return;
81   m_started = true;
82   for(tServices::const_iterator it = m_services.begin(); it != m_services.end(); ++it)
83     doAddServiceType(*it);
84 }
85
86 void CZeroconfBrowser::Stop()
87 {
88   CSingleLock lock(*mp_crit_sec);
89   if(!m_started)
90     return;
91   for(tServices::iterator it = m_services.begin(); it != m_services.end(); ++it)
92     RemoveServiceType(*it);
93   m_started = false;
94 }
95
96 bool CZeroconfBrowser::AddServiceType(const CStdString& fcr_service_type /*const CStdString& domain*/ )
97 {
98   CSingleLock lock(*mp_crit_sec);
99   std::pair<tServices::iterator, bool> ret = m_services.insert(fcr_service_type);
100   if(!ret.second)
101   {
102     //service already in list
103     return false;
104   }
105   if(m_started)
106     return doAddServiceType(*ret.first);
107   //not yet started, so its just queued
108   return true;
109 }
110
111 bool CZeroconfBrowser::RemoveServiceType(const CStdString& fcr_service_type)
112 {
113   CSingleLock lock(*mp_crit_sec);
114   tServices::iterator ret = m_services.find(fcr_service_type);
115   if(ret == m_services.end())
116     return false;
117   if(m_started)
118     return doRemoveServiceType(fcr_service_type);
119   //not yet started, so its just queued
120   return true;
121 }
122
123 std::vector<CZeroconfBrowser::ZeroconfService> CZeroconfBrowser::GetFoundServices()
124 {
125   CSingleLock lock(*mp_crit_sec);
126   if(m_started)
127     return doGetFoundServices();
128   else
129   {
130     CLog::Log(LOGDEBUG, "CZeroconfBrowser::GetFoundServices asked for services without browser running");
131     return std::vector<ZeroconfService>();
132   }
133 }
134
135 bool CZeroconfBrowser::ResolveService(ZeroconfService& fr_service, double f_timeout)
136 {
137   CSingleLock lock(*mp_crit_sec);
138   if(m_started)
139   {
140     return doResolveService(fr_service, f_timeout);
141   }
142   CLog::Log(LOGDEBUG, "CZeroconfBrowser::GetFoundServices asked for services without browser running");
143   return false;
144 }
145
146 CZeroconfBrowser*  CZeroconfBrowser::GetInstance()
147 {
148   if(!smp_instance)
149   {
150     //use double checked locking
151     CAtomicSpinLock lock(sm_singleton_guard);
152     if(!smp_instance)
153     {
154 #if !defined(HAS_ZEROCONF)
155       smp_instance = new CZeroconfBrowserDummy;
156 #else
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;
163 #endif
164 #endif
165     }
166   }
167   assert(smp_instance);
168   return smp_instance;
169 }
170
171 void CZeroconfBrowser::ReleaseInstance()
172 {
173   CAtomicSpinLock lock(sm_singleton_guard);
174   delete smp_instance;
175   smp_instance = 0;
176 }
177
178
179 CZeroconfBrowser::ZeroconfService::ZeroconfService():m_port(0){}
180
181 CZeroconfBrowser::ZeroconfService::ZeroconfService(const CStdString& fcr_name, const CStdString& fcr_type, const CStdString& fcr_domain):
182   m_name(fcr_name),
183   m_domain(fcr_domain),
184   m_port(0)
185 {
186   SetType(fcr_type);
187 }
188 void CZeroconfBrowser::ZeroconfService::SetName(const CStdString& fcr_name)
189 {
190   m_name = fcr_name;
191 }
192
193 void CZeroconfBrowser::ZeroconfService::SetType(const CStdString& fcr_type)
194 {
195   if(fcr_type.empty())
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 + ".";
200   else
201     m_type = fcr_type;
202 }
203
204 void CZeroconfBrowser::ZeroconfService::SetDomain(const CStdString& fcr_domain)
205 {
206   m_domain = fcr_domain;
207 }
208
209 void CZeroconfBrowser::ZeroconfService::SetHostname(const CStdString& fcr_hostname)
210 {
211   m_hostname = fcr_hostname;
212 }
213
214 void CZeroconfBrowser::ZeroconfService::SetIP(const CStdString& fcr_ip)
215 {
216   m_ip = fcr_ip;
217 }
218
219 void CZeroconfBrowser::ZeroconfService::SetPort(int f_port)
220 {
221   m_port = f_port;
222 }
223
224 void CZeroconfBrowser::ZeroconfService::SetTxtRecords(const tTxtRecordMap& txt_records)
225 {
226   m_txtrecords_map = txt_records;
227   
228   CLog::Log(LOGDEBUG,"CZeroconfBrowser: dump txt-records");
229   for(tTxtRecordMap::const_iterator it = m_txtrecords_map.begin(); it != m_txtrecords_map.end(); ++it)
230   {
231     CLog::Log(LOGDEBUG,"CZeroconfBrowser:  key: %s value: %s",it->first.c_str(), it->second.c_str());
232   }
233 }
234
235 CStdString CZeroconfBrowser::ZeroconfService::toPath(const ZeroconfService& fcr_service)
236 {
237   return CStdString(fcr_service.m_type + "@" + fcr_service.m_domain + "@" + fcr_service.m_name);
238 }
239
240 CZeroconfBrowser::ZeroconfService CZeroconfBrowser::ZeroconfService::fromPath(const CStdString& fcr_path)
241 {
242   if( fcr_path.empty() )
243     throw std::runtime_error("CZeroconfBrowser::ZeroconfService::fromPath input string empty!");
244
245   int pos1 = fcr_path.Find('@'); //first @
246   int pos2 = fcr_path.Find('@', pos1+1); //second
247
248   if( pos1 == -1 || pos2 == -1 )
249     throw std::runtime_error("CZeroconfBrowser::ZeroconfService::fromPath invalid input path");
250
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
255     );
256 }