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
44namespace 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
100void
101ServiceChooserDialog::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 */
166void
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 */
203void
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 */
228void
229ServiceChooserDialog::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 */
258void
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 */
275void
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.