service_model.cpp

00001 
00002 /***************************************************************************
00003  *  service_model.cpp - Manages list of discovered services of given type
00004  *
00005  *  Created: Mon Sep 29 16:37:14 2008
00006  *  Copyright  2008  Daniel Beck
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <gui_utils/service_model.h>
00025 #include <netcomm/dns-sd/avahi_thread.h>
00026 
00027 #include <sys/types.h>
00028 #include <sys/socket.h>
00029 #include <arpa/inet.h>
00030 
00031 using namespace std;
00032 using namespace fawkes;
00033 
00034 /** @class fawkes::ServiceModel::ServiceRecord gui_utils/service_model.h
00035  * Detects services and manages information about detected services.
00036  *
00037  * @author Daniel Beck
00038  */
00039 
00040 /** @class fawkes::ServiceModel gui_utils/service_model.h 
00041  * Abstract base class for widgets that allow to view the detected
00042  * services of a certain type.
00043  *
00044  * @author Daniel Beck
00045  */
00046 
00047 /** @var fawkes::ServiceModel::m_service_list
00048  * Storage object.
00049  */
00050 
00051 /** @var fawkes::ServiceModel::m_service_record
00052  * Column record class
00053  */
00054 
00055 /** @var fawkes::ServiceModel::m_avahi
00056  * Avahi thread.
00057  */
00058 
00059 /** @var fawkes::ServiceModel::m_signal_service_added
00060  * This signal is emitted whenever a new service has been added.
00061  */
00062 
00063 /** @var fawkes::ServiceModel::m_signal_service_removed
00064  * This signal is emitted whenever a service is removed
00065  */
00066 
00067 /** @struct fawkes::ServiceModel::ServiceAddedRecord
00068  * Data structure to hold information about a newly added services.
00069  */ 
00070 
00071 /** @struct fawkes::ServiceModel::ServiceRemovedRecord
00072  * Data structure to hold information about a recently removed services.
00073  */ 
00074 
00075 /** @var fawkes::ServiceModel::m_added_services
00076  * Queue that holds the newly added services.
00077  */
00078 
00079 /** @var fawkes::ServiceModel::m_removed_services
00080  * Queue that holds the recently removed services.
00081  */
00082 
00083 /** Constructor.
00084  * @param service the service identifier
00085  */
00086 ServiceModel::ServiceModel(const char* service)
00087 {
00088   m_service_list = Gtk::ListStore::create(m_service_record);
00089 
00090   m_avahi = new AvahiThread();
00091   m_avahi->watch_service(service, this);
00092   m_avahi->start();
00093 
00094   m_own_avahi_thread = true;
00095 
00096   m_signal_service_added.connect( sigc::mem_fun(*this, &ServiceModel::on_service_added) );
00097   m_signal_service_removed.connect( sigc::mem_fun(*this, &ServiceModel::on_service_removed) );
00098 }
00099 
00100 /** Constructor.
00101  * @param avahi_thread an AvahiThread that already watches for the
00102  * desired type of services
00103  */
00104 ServiceModel::ServiceModel(fawkes::AvahiThread* avahi_thread)
00105 {
00106   m_service_list = Gtk::ListStore::create(m_service_record);
00107 
00108   m_avahi = avahi_thread;
00109   m_own_avahi_thread = false;
00110 }
00111 
00112 /** Destructor. */
00113 ServiceModel::~ServiceModel()
00114 {
00115   if (m_own_avahi_thread)
00116     { 
00117       m_avahi->cancel();
00118       m_avahi->join();
00119       delete m_avahi;
00120     }
00121 }
00122 
00123 /** Get a reference to the model.
00124  * @return a reference to the model
00125  */
00126 Glib::RefPtr<Gtk::ListStore>&
00127 ServiceModel::get_list_store()
00128 {
00129   return m_service_list;
00130 }
00131 
00132 /** Access the column record.
00133  * @return the column record
00134  */
00135 ServiceModel::ServiceRecord&
00136 ServiceModel::get_column_record()
00137 {
00138   return m_service_record;
00139 }
00140 
00141 void
00142 ServiceModel::all_for_now()
00143 {
00144 }
00145 
00146 void
00147 ServiceModel::cache_exhausted()
00148 {
00149 }
00150 
00151 void
00152 ServiceModel::browse_failed( const char* name,
00153                             const char* type,
00154                             const char* domain )
00155 {
00156 }
00157 
00158 void
00159 ServiceModel::service_added( const char* name,
00160                             const char* type,
00161                             const char* domain,
00162                             const char* host_name,
00163                             const struct sockaddr* addr,
00164                             const socklen_t addr_size,
00165                             uint16_t port,
00166                             std::list<std::string>& txt,
00167                             int flags )
00168 {
00169   ServiceAddedRecord s;
00170   char ipaddr[INET_ADDRSTRLEN];
00171   struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
00172   s.name = string(name);
00173   s.type = string(type);
00174   s.domain = string(domain);
00175   s.hostname = string(host_name);
00176   s.ipaddr = inet_ntop(AF_INET, &(saddr->sin_addr), ipaddr, sizeof(ipaddr));
00177   s.port = port;
00178 
00179   m_added_services.push_locked(s);
00180 
00181   m_signal_service_added();
00182 }
00183 
00184 void
00185 ServiceModel::service_removed( const char* name,
00186                               const char* type,
00187                               const char* domain )
00188 {
00189   ServiceRemovedRecord s;
00190   s.name = string(name);
00191   s.type = string(type);
00192   s.domain = string(domain);
00193 
00194   m_removed_services.push_locked(s);
00195 
00196   m_signal_service_removed();
00197 }
00198 
00199 /** Signal handler for the service-added signal. */
00200 void
00201 ServiceModel::on_service_added()
00202 {
00203   m_added_services.lock();
00204 
00205   while ( !m_added_services.empty() )
00206     {
00207       ServiceAddedRecord& s  = m_added_services.front();
00208 
00209       Gtk::TreeModel::Row row = *m_service_list->append();
00210       
00211       row[m_service_record.name]     = s.name;
00212       row[m_service_record.type]     = s.type;
00213       row[m_service_record.domain]   = s.domain;
00214       row[m_service_record.hostname] = s.hostname;
00215       row[m_service_record.ipaddr]   = s.ipaddr;
00216       row[m_service_record.port]     = s.port;
00217 
00218       m_added_services.pop();
00219     }
00220 
00221   m_added_services.unlock();
00222 }
00223 
00224 /** Signal handler for the service-removed signal. */
00225 void
00226 ServiceModel::on_service_removed()
00227 {
00228   m_removed_services.lock();
00229 
00230   while ( !m_removed_services.empty() )
00231     {
00232       ServiceRemovedRecord& s = m_removed_services.front();
00233 
00234       Gtk::TreeIter iter;
00235       iter = m_service_list->children().begin();
00236       
00237       while ( iter != m_service_list->children().end() )
00238         {
00239           Gtk::TreeModel::Row row = *iter;
00240           if ( (row[m_service_record.name]   == s.name) &&
00241                (row[m_service_record.type]   == s.type) &&
00242                (row[m_service_record.domain] == s.domain) )
00243             {
00244               iter = m_service_list->erase(iter);
00245               m_service_list->row_deleted( m_service_list->get_path(iter) );
00246             }
00247           else
00248             { ++iter; }
00249         }
00250 
00251       m_removed_services.pop();
00252     }
00253 
00254   m_removed_services.unlock();
00255 }