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