Fawkes API  Fawkes Development Version
service_chooser_dialog.cpp
1 
2 /***************************************************************************
3  * service_chooser_dialog.cpp - Dialog for choosing a network service
4  *
5  * Created: Sun Oct 12 17:06:06 2008 (split from lasergui_hildon.cpp)
6  * Copyright 2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <arpa/inet.h>
25 #include <core/exception.h>
26 #include <core/exceptions/software.h>
27 #include <gui_utils/service_chooser_dialog.h>
28 #include <gui_utils/service_model.h>
29 #include <netcomm/fawkes/client.h>
30 #include <netcomm/utils/resolver.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <sys/types.h>
34 #include <utils/system/argparser.h>
35 
36 #include <algorithm>
37 #include <cstring>
38 
39 #ifdef HAVE_GCONFMM
40 # define GCONF_DIR "/apps/fawkes/service_chooser_dialog"
41 # define GCONF_PREFIX GCONF_DIR "/"
42 #endif
43 
44 namespace fawkes {
45 
46 /** @class ServiceChooserDialog <gui_utils/service_chooser_dialog.h>
47  * Service chooser dialog.
48  * Allows to choose a service discovered via Avahi. Use the run routine,
49  * it returns 1 if a service was selected or 0 if no service was found or
50  * the selection was cancelled. The dialog is always modal.
51  * @author Tim Niemueller
52  */
53 
54 /** Constructor.
55  * @param parent parent window
56  * @param title title of the dialog
57  * @param service service string
58  */
60  Glib::ustring title,
61  const char * service)
62 : Gtk::Dialog(title, parent, /* modal */ true), parent_(parent), expander_("Manual entry")
63 {
64  service_model_ = new ServiceModel(service);
65  ctor();
66  client_ = NULL;
67 }
68 
69 /** Constructor.
70  * @param parent parent window
71  * @param client Fawkes network client to connect on run()
72  * @param title title of the dialog
73  * @param service service string
74  */
76  FawkesNetworkClient *client,
77  Glib::ustring title,
78  const char * service)
79 : Gtk::Dialog(title, parent, /* modal */ true), parent_(parent), expander_("Manual entry")
80 {
81  service_model_ = new ServiceModel(service);
82  ctor();
83  client_ = client;
84 }
85 
86 /** Destructor. */
88 {
89 #ifdef HAVE_GCONFMM
90  if (expander_.get_expanded() && !treeview_.has_focus() && entry_.get_text_length() > 0) {
91  gconf_->set(GCONF_PREFIX "manual_host", entry_.get_text());
92  gconf_->set(GCONF_PREFIX "manual_expanded", true);
93  } else {
94  gconf_->set(GCONF_PREFIX "manual_expanded", false);
95  }
96 #endif
97  delete service_model_;
98 }
99 
100 void
101 ServiceChooserDialog::ctor()
102 {
103  set_default_size(480, 300);
104 
105  treeview_.set_model(service_model_->get_list_store());
106  treeview_.append_column("Service", service_model_->get_column_record().name);
107  treeview_.append_column("Address/Port", service_model_->get_column_record().addrport);
108  scrollwin_.add(treeview_);
109  scrollwin_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
110  treeview_.show();
111  expander_.add(entry_);
112  entry_.show();
113  entry_.set_activates_default(true);
114 
115  Glib::ustring default_host("localhost");
116 #ifdef HAVE_GCONFMM
117  gconf_ = Gnome::Conf::Client::get_default_client();
118  gconf_->add_dir(GCONF_DIR);
119  Gnome::Conf::Value host_val = gconf_->get_without_default(GCONF_PREFIX "manual_host");
120  if (host_val.get_type() == Gnome::Conf::VALUE_STRING) {
121  default_host = host_val.get_string();
122  }
123 #endif
124 
125  char *fawkes_ip = getenv("FAWKES_IP");
126  if (fawkes_ip)
127  entry_.set_text(fawkes_ip);
128  else
129  entry_.set_text(default_host);
130 
131  Gtk::Box *vbox = get_vbox();
132  vbox->pack_start(scrollwin_);
133  vbox->pack_end(expander_, Gtk::PACK_SHRINK);
134  scrollwin_.show();
135  expander_.show();
136 
137  add_button(Gtk::Stock::CANCEL, 0);
138  add_button(Gtk::Stock::OK, 1);
139 
140  set_default_response(1);
141 
142  treeview_.signal_row_activated().connect(
143  sigc::bind(sigc::hide<0>(sigc::hide<0>(sigc::mem_fun(*this, &ServiceChooserDialog::response))),
144  1));
145 
146 #ifdef GLIBMM_PROPERTIES_ENABLED
147  expander_.property_expanded().signal_changed().connect(
148  sigc::mem_fun(*this, &ServiceChooserDialog::on_expander_changed));
149 #endif
150 
151 #ifdef HAVE_GCONFMM
152  if (gconf_->get_bool(GCONF_PREFIX "manual_expanded")) {
153  expander_.set_expanded(true);
154  }
155 #endif
156 }
157 
158 /** Get selected service.
159  * If a service has been selected use this method to get the IP Address as
160  * string of the host that has the service and the port.
161  * @param name name of the service
162  * @param hostname hostname of the host associated with the service
163  * @param port upon successful return contains the port
164  * @exception Exception thrown if no service has been selected
165  */
166 void
168  Glib::ustring & hostname,
169  unsigned short int &port)
170 {
171  Glib::RefPtr<Gtk::TreeSelection> treesel = treeview_.get_selection();
172  if (expander_.get_expanded() && !treeview_.has_focus()) {
173  if (entry_.get_text_length() > 0) {
174  std::string tmp_hostname;
175  ArgumentParser::parse_hostport_s(entry_.get_text().c_str(), tmp_hostname, port);
176  hostname = tmp_hostname;
177  name = hostname;
178  return;
179  }
180  }
181 
182  Gtk::TreeModel::iterator iter = treesel->get_selected();
183  if (iter) {
184  Gtk::TreeModel::Row row = *iter;
185  name = row[service_model_->get_column_record().name];
186  hostname = row[service_model_->get_column_record().ipaddr];
187  port = row[service_model_->get_column_record().port];
188 
189  } else {
190  throw Exception("No host selected");
191  }
192 }
193 
194 /** Get selected service.
195  * If a service has been selected use this method to get the IP Address as
196  * string of the host that has the service and the port.
197  * May not be called for manual entry since this would require hostname resolution within
198  * the dialog. That should be handled on the caller's side.
199  * @param hostname hostname of the host associated with the service
200  * @param sockaddr upon successful return contains the sockaddr structure of the specific endpoint
201  * @exception Exception thrown if no service has been selected
202  */
203 void
205  struct sockaddr_storage &sockaddr)
206 {
207  Glib::RefPtr<Gtk::TreeSelection> treesel = treeview_.get_selection();
208  if (expander_.get_expanded() && !treeview_.has_focus() && entry_.get_text_length() > 0) {
209  throw Exception("May not be called for manual entry");
210  }
211 
212  Gtk::TreeModel::iterator iter = treesel->get_selected();
213  if (iter) {
214  Gtk::TreeModel::Row row = *iter;
215  hostname = row[service_model_->get_column_record().ipaddr];
216  sockaddr = row[service_model_->get_column_record().sockaddr];
217 
218  } else {
219  throw Exception("No host selected");
220  }
221 }
222 
223 /** Get raw address.
224  * @param addr upon returns contains the raw representation of the IP address
225  * @param addr_size size in bytes of addr, if addr_size is too small for an
226  * AF_INET addr an exception is thrown.
227  */
228 void
229 ServiceChooserDialog::get_raw_address(struct sockaddr *addr, socklen_t addr_size)
230 {
231  /*
232  if ( addr_size < sizeof(struct sockaddr_in) ) {
233  throw Exception("Size of addrlen too small, only %u bytes, but required %zu\n",
234  addr_size, sizeof(struct sockaddr_in));
235  }
236  Glib::ustring name, hostname;
237  unsigned short int port;
238  get_selected_service (name, hostname, ipaddr, port);
239 
240  if (inet_pton(AF_INET, ipaddr.c_str(), &(((struct sockaddr_in *)addr)->sin_addr)) <= 0) {
241  NetworkNameResolver resolver;
242  struct sockaddr_in *saddr;
243  socklen_t saddr_len;
244  if (resolver.resolve_name_blocking(ipaddr.c_str(), (struct sockaddr **)&saddr, &saddr_len)) {
245  memcpy(addr, saddr, std::min(saddr_len, addr_size));
246  } else {
247  throw Exception("Could not lookup hostname '%s' and it is not a valid IP address",
248  ipaddr.c_str());
249  }
250  }
251  */
252 }
253 
254 /** Signal handler for expander event.
255  * Called when expander is (de-)expanded. Only works with Glibmm properties
256  * enabled, i.e. not on Maemo.
257  */
258 void
260 {
261  if (expander_.get_expanded()) {
262  entry_.grab_focus();
263  } else {
264  treeview_.grab_focus();
265  }
266 }
267 
268 /** Run dialog and try to connect.
269  * This runs the service chooser dialog and connects to the given service
270  * with the attached FawkesNetworkClient. If the connection couldn't be established
271  * an error dialog is shown. You should not rely on the connection to be
272  * active after calling this method, rather you should use a ConnectionDispatcher
273  * to get the "connected" signal.
274  */
275 void
277 {
278  if (!client_)
279  throw NullPointerException("FawkesNetworkClient not set");
280  if (client_->connected())
281  throw Exception("Client is already connected");
282 
283  if (run()) {
284  try {
285  if (expander_.get_expanded() && !treeview_.has_focus() && entry_.get_text_length() > 0) {
286  Glib::ustring name, hostname;
287  unsigned short int port;
288  get_selected_service(name, hostname, port);
289  client_->connect(hostname.c_str(), port);
290 
291  } else {
292  struct sockaddr_storage sockaddr;
293  Glib::ustring hostname;
294  get_selected_service(hostname, sockaddr);
295  client_->connect(hostname.c_str(), sockaddr);
296  }
297  } catch (Exception &e) {
298  Glib::ustring message = *(e.begin());
299  Gtk::MessageDialog md(parent_,
300  message,
301  /* markup */ false,
302  Gtk::MESSAGE_ERROR,
303  Gtk::BUTTONS_OK,
304  /* modal */ true);
305  md.set_title("Connection failed");
306  md.run();
307  }
308  }
309 }
310 
311 } // end of namespace fawkes
static void parse_hostport_s(const char *s, char **host, unsigned short int *port)
Parse host:port string.
Definition: argparser.cpp:251
Base class for exceptions in Fawkes.
Definition: exception.h:36
iterator begin() noexcept
Get iterator for messages.
Definition: exception.cpp:676
Simple Fawkes network client.
Definition: client.h:52
void connect()
Connect to remote.
Definition: client.cpp:424
bool connected() const noexcept
Check if connection is alive.
Definition: client.cpp:828
A NULL pointer was supplied where not allowed.
Definition: software.h:32
void get_raw_address(struct sockaddr *addr, socklen_t addr_size)
Get raw address.
ServiceChooserDialog(Gtk::Window &parent, FawkesNetworkClient *client, Glib::ustring title="Select Service", const char *service="_fawkes._tcp")
Constructor.
virtual ~ServiceChooserDialog()
Destructor.
virtual void on_expander_changed()
Signal handler for expander event.
void run_and_connect()
Run dialog and try to connect.
void get_selected_service(Glib::ustring &name, Glib::ustring &hostname, unsigned short int &port)
Get selected service.
Gtk::TreeModelColumn< unsigned short > port
The port the service is running on.
Definition: service_model.h:69
Gtk::TreeModelColumn< Glib::ustring > addrport
Address:port string.
Definition: service_model.h:70
Gtk::TreeModelColumn< Glib::ustring > name
The name of the service.
Definition: service_model.h:60
Gtk::TreeModelColumn< struct sockaddr_storage > sockaddr
sockaddr structure
Definition: service_model.h:71
Gtk::TreeModelColumn< Glib::ustring > ipaddr
The IP address as string of the host the service is running on.
Definition: service_model.h:68
Abstract base class for widgets that allow to view the detected services of a certain type.
Definition: service_model.h:36
ServiceRecord & get_column_record()
Access the column record.
Glib::RefPtr< Gtk::ListStore > & get_list_store()
Get a reference to the model.
Fawkes library namespace.