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 
31 #include <algorithm>
32 #include <cstring>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <arpa/inet.h>
36 #include <netinet/in.h>
37 
38 namespace fawkes {
39 #if 0 /* just to make Emacs auto-indent happy */
40 }
41 #endif
42 
43 /** @class ServiceChooserDialog <gui_utils/service_chooser_dialog.h>
44  * Service chooser dialog.
45  * Allows to choose a service discovered via Avahi. Use the run routine,
46  * it returns 1 if a service was selected or 0 if no service was found or
47  * the selection was cancelled. The dialog is always modal.
48  * @author Tim Niemueller
49  */
50 
51 /** Constructor.
52  * @param parent parent window
53  * @param title title of the dialog
54  * @param service service string
55  */
56 ServiceChooserDialog::ServiceChooserDialog(Gtk::Window &parent,
57  Glib::ustring title,
58  const char *service)
59  : Gtk::Dialog(title, parent, /* modal */ true),
60  __parent(parent), __expander("Manual entry")
61 {
62  __service_model = new ServiceModel(service);
63  ctor();
64  __client = NULL;
65 }
66 
67 
68 /** Constructor.
69  * @param parent parent window
70  * @param client Fawkes network client to connect on run()
71  * @param title title of the dialog
72  * @param service service string
73  */
75  FawkesNetworkClient *client,
76  Glib::ustring title,
77  const char *service)
78  : Gtk::Dialog(title, parent, /* modal */ true),
79  __parent(parent), __expander("Manual entry")
80 {
81  __service_model = new ServiceModel(service);
82  ctor();
83  __client = client;
84 }
85 
86 
87 /** Destructor. */
89 {
90  delete __service_model;
91 }
92 
93 
94 void
95 ServiceChooserDialog::ctor()
96 {
97  set_default_size(360, 240);
98 
99  __treeview.set_model(__service_model->get_list_store());
100  __treeview.append_column("Service", __service_model->get_column_record().name);
101  __treeview.append_column("IP Address", __service_model->get_column_record().ipaddr);
102  __scrollwin.add(__treeview);
103  __scrollwin.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
104  __treeview.show();
105  __expander.add(__entry);
106  __entry.show();
107  __entry.set_activates_default(true);
108 
109  char * fawkes_ip = getenv("FAWKES_IP");
110  if (fawkes_ip) __entry.set_text(fawkes_ip);
111  else __entry.set_text("localhost");
112 
113  Gtk::Box *vbox = get_vbox();
114  vbox->pack_start(__scrollwin);
115  vbox->pack_end(__expander, Gtk::PACK_SHRINK);
116  __scrollwin.show();
117  __expander.show();
118 
119  add_button(Gtk::Stock::CANCEL, 0);
120  add_button(Gtk::Stock::OK, 1);
121 
122  set_default_response(1);
123 
124  __treeview.signal_row_activated().connect(sigc::bind(sigc::hide<0>(sigc::hide<0>(sigc::mem_fun(*this, &ServiceChooserDialog::response))), 1));
125 
126 #ifdef GLIBMM_PROPERTIES_ENABLED
127  __expander.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &ServiceChooserDialog::on_expander_changed));
128 #endif
129 }
130 
131 
132 /** Get selected service.
133  * If a service has been selected use this method to get the IP Address as
134  * string of the host that has the service and the port.
135  * @param name name of the service
136  * @param hostname hostname of the host associated with the service
137  * @param ipaddr upon successful return contains the IP address as string
138  * @param port upon successful return contains the port
139  * @exception Exception thrown if no service has been selected
140  */
141 void
143  Glib::ustring &hostname,
144  Glib::ustring &ipaddr,
145  unsigned short int &port)
146 {
147  Glib::RefPtr<Gtk::TreeSelection> treesel = __treeview.get_selection();
148  if (__expander.get_expanded() && !__treeview.has_focus()) {
149  if ( __entry.get_text_length() > 0 ) {
150  char *tmpvalue = strdup(__entry.get_text().c_str());
151 
152  if ( strchr(tmpvalue, ':') != NULL ) {
153  char *save_ptr;
154  hostname = strtok_r(tmpvalue, ":", &save_ptr);
155  char *tmpport = strtok_r(NULL, "", &save_ptr);
156 
157  int port_num = atoi(tmpport);
158  if ( (port_num < 0) || (port_num > 0xFFFF) ) {
159  throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF);
160  }
161  port = port_num;
162  } else {
163  hostname = tmpvalue;
164  port = 0;
165  }
166 
167  // evil, but Glib::Regex is only availabel in ver >= 2.14, n/a on maemo
168  ipaddr = hostname;
169 
170  name = hostname;
171 
172  free(tmpvalue);
173  return;
174  }
175  }
176 
177  Gtk::TreeModel::iterator iter = treesel->get_selected();
178  if (iter) {
179  Gtk::TreeModel::Row row = *iter;
180  name = row[__service_model->get_column_record().name];
181  hostname = row[__service_model->get_column_record().hostname];
182  ipaddr = row[__service_model->get_column_record().ipaddr];
183  port = row[__service_model->get_column_record().port];
184 
185  } else {
186  throw Exception("No host selected");
187  }
188 }
189 
190 
191 /** Get raw address.
192  * @param addr upon returns contains the raw representation of the IP address
193  * @param addr_size size in bytes of addr, if addr_size is too small for an
194  * AF_INET addr an exception is thrown.
195  */
196 void
197 ServiceChooserDialog::get_raw_address(struct sockaddr *addr, socklen_t addr_size)
198 {
199  if ( addr_size < sizeof(struct sockaddr_in) ) {
200  throw Exception("Size of addrlen too small, only %u bytes, but required %zu\n",
201  addr_size, sizeof(struct sockaddr_in));
202  }
203  Glib::ustring name, hostname, ipaddr;
204  unsigned short int port;
205  get_selected_service (name, hostname, ipaddr, port);
206 
207  if (inet_pton(AF_INET, ipaddr.c_str(), &(((struct sockaddr_in *)addr)->sin_addr)) <= 0) {
208  NetworkNameResolver resolver;
209  struct sockaddr_in *saddr;
210  socklen_t saddr_len;
211  if (resolver.resolve_name_blocking(ipaddr.c_str(), (struct sockaddr **)&saddr, &saddr_len)) {
212  memcpy(addr, saddr, std::min(saddr_len, addr_size));
213  } else {
214  throw Exception("Could not lookup hostname '%s' and it is not a valid IP address",
215  ipaddr.c_str());
216  }
217  }
218 
219 }
220 
221 
222 /** Signal handler for expander event.
223  * Called when expander is (de-)expanded. Only works with Glibmm properties
224  * enabled, i.e. not on Maemo.
225  */
226 void
228 {
229  if (__expander.get_expanded()) {
230  __entry.grab_focus();
231  } else {
232  __treeview.grab_focus();
233  }
234 }
235 
236 
237 /** Run dialog and try to connect.
238  * This runs the service chooser dialog and connects to the given service
239  * with the attached FawkesNetworkClient. If the connection couldn't be established
240  * an error dialog is shown. You should not rely on the connection to be
241  * active after calling this method, rather you should use a ConnectionDispatcher
242  * to get the "connected" signal.
243  */
244 void
246 {
247  if (! __client) throw NullPointerException("FawkesNetworkClient not set");
248  if (__client->connected()) throw Exception("Client is already connected");
249 
250  if ( run() ) {
251  try {
252  Glib::ustring name;
253  Glib::ustring hostname;
254  Glib::ustring ipaddr;
255  unsigned short int port;
256  get_selected_service(name, hostname, ipaddr, port);
257  if ( port == 0 ) port = 1910;
258 
259  __client->connect(hostname.c_str(), ipaddr.c_str(), port);
260  } catch (Exception &e) {
261  Glib::ustring message = *(e.begin());
262  Gtk::MessageDialog md(__parent, message, /* markup */ false,
263  Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK,
264  /* modal */ true);
265  md.set_title("Connection failed");
266  md.run();
267  }
268  }
269 }
270 
271 } // 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.
Gtk::TreeModelColumn< Glib::ustring > hostname
The name of the host the service is running on.
Definition: service_model.h:59
Simple Fawkes network client.
Definition: client.h:51
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:415
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
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:678
bool connected() const
Check if connection is alive.
Definition: client.cpp:797
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:61
virtual ~ServiceChooserDialog()
Destructor.
Network name and address resolver.
Definition: resolver.h:48
Gtk::TreeModelColumn< Glib::ustring > name
The name of the service.
Definition: service_model.h:56
bool resolve_name_blocking(const char *name, struct sockaddr **addr, socklen_t *addrlen)
Resolve name and wait for the result.
Definition: resolver.cpp:219
Index out of bounds.
Definition: software.h:88
Gtk::TreeModelColumn< Glib::ustring > ipaddr
The IP address as string of the host the service is running on.
Definition: service_model.h:60
void get_selected_service(Glib::ustring &name, Glib::ustring &hostname, Glib::ustring &ipaddr, unsigned short int &port)
Get selected service.