Fawkes API Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * service_chooser_dialog.cpp - Dialog for choosing a network service 00004 * 00005 * Created: Sun Oct 12 17:06:06 2008 (split from lasergui_hildon.cpp) 00006 * Copyright 2008 Tim Niemueller [www.niemueller.de] 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 <core/exception.h> 00025 #include <core/exceptions/software.h> 00026 #include <netcomm/fawkes/client.h> 00027 #include <netcomm/utils/resolver.h> 00028 #include <gui_utils/service_chooser_dialog.h> 00029 #include <gui_utils/service_model.h> 00030 00031 #include <algorithm> 00032 #include <cstring> 00033 #include <sys/types.h> 00034 #include <sys/socket.h> 00035 #include <arpa/inet.h> 00036 #include <netinet/in.h> 00037 00038 namespace fawkes { 00039 #if 0 /* just to make Emacs auto-indent happy */ 00040 } 00041 #endif 00042 00043 /** @class ServiceChooserDialog <gui_utils/service_chooser_dialog.h> 00044 * Service chooser dialog. 00045 * Allows to choose a service discovered via Avahi. Use the run routine, 00046 * it returns 1 if a service was selected or 0 if no service was found or 00047 * the selection was cancelled. The dialog is always modal. 00048 * @author Tim Niemueller 00049 */ 00050 00051 /** Constructor. 00052 * @param parent parent window 00053 * @param title title of the dialog 00054 * @param service service string 00055 */ 00056 ServiceChooserDialog::ServiceChooserDialog(Gtk::Window &parent, 00057 Glib::ustring title, 00058 const char *service) 00059 : Gtk::Dialog(title, parent, /* modal */ true, /* separator */ true), 00060 __parent(parent), __expander("Manual entry") 00061 { 00062 __service_model = new ServiceModel(service); 00063 ctor(); 00064 __client = NULL; 00065 } 00066 00067 00068 /** Constructor. 00069 * @param parent parent window 00070 * @param client Fawkes network client to connect on run() 00071 * @param title title of the dialog 00072 * @param service service string 00073 */ 00074 ServiceChooserDialog::ServiceChooserDialog(Gtk::Window &parent, 00075 FawkesNetworkClient *client, 00076 Glib::ustring title, 00077 const char *service) 00078 : Gtk::Dialog(title, parent, /* modal */ true, /* separator */ true), 00079 __parent(parent), __expander("Manual entry") 00080 { 00081 __service_model = new ServiceModel(service); 00082 ctor(); 00083 __client = client; 00084 } 00085 00086 00087 /** Destructor. */ 00088 ServiceChooserDialog::~ServiceChooserDialog() 00089 { 00090 delete __service_model; 00091 } 00092 00093 00094 void 00095 ServiceChooserDialog::ctor() 00096 { 00097 set_default_size(360, 240); 00098 00099 __treeview.set_model(__service_model->get_list_store()); 00100 __treeview.append_column("Service", __service_model->get_column_record().name); 00101 __treeview.append_column("IP Address", __service_model->get_column_record().ipaddr); 00102 __scrollwin.add(__treeview); 00103 __scrollwin.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); 00104 __treeview.show(); 00105 __expander.add(__entry); 00106 __entry.show(); 00107 __entry.set_activates_default(true); 00108 00109 char * fawkes_ip = getenv("FAWKES_IP"); 00110 if (fawkes_ip) __entry.set_text(fawkes_ip); 00111 else __entry.set_text("localhost"); 00112 00113 Gtk::VBox *vbox = get_vbox(); 00114 vbox->pack_start(__scrollwin); 00115 vbox->pack_end(__expander, Gtk::PACK_SHRINK); 00116 __scrollwin.show(); 00117 __expander.show(); 00118 00119 add_button(Gtk::Stock::CANCEL, 0); 00120 add_button(Gtk::Stock::OK, 1); 00121 00122 set_default_response(1); 00123 00124 __treeview.signal_row_activated().connect(sigc::bind(sigc::hide<0>(sigc::hide<0>(sigc::mem_fun(*this, &ServiceChooserDialog::response))), 1)); 00125 00126 #ifdef GLIBMM_PROPERTIES_ENABLED 00127 __expander.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &ServiceChooserDialog::on_expander_changed)); 00128 #endif 00129 } 00130 00131 00132 /** Get selected service. 00133 * If a service has been selected use this method to get the IP Address as 00134 * string of the host that has the service and the port. 00135 * @param name name of the service 00136 * @param hostname hostname of the host associated with the service 00137 * @param ipaddr upon successful return contains the IP address as string 00138 * @param port upon successful return contains the port 00139 * @exception Exception thrown if no service has been selected 00140 */ 00141 void 00142 ServiceChooserDialog::get_selected_service(Glib::ustring &name, 00143 Glib::ustring &hostname, 00144 Glib::ustring &ipaddr, 00145 unsigned short int &port) 00146 { 00147 Glib::RefPtr<Gtk::TreeSelection> treesel = __treeview.get_selection(); 00148 if (__expander.get_expanded() && !__treeview.has_focus()) { 00149 if ( __entry.get_text_length() > 0 ) { 00150 char *tmpvalue = strdup(__entry.get_text().c_str()); 00151 00152 if ( strchr(tmpvalue, ':') != NULL ) { 00153 char *save_ptr; 00154 hostname = strtok_r(tmpvalue, ":", &save_ptr); 00155 char *tmpport = strtok_r(NULL, "", &save_ptr); 00156 00157 int port_num = atoi(tmpport); 00158 if ( (port_num < 0) || (port_num > 0xFFFF) ) { 00159 throw OutOfBoundsException("Invalid port", port_num, 0, 0xFFFF); 00160 } 00161 port = port_num; 00162 } else { 00163 hostname = tmpvalue; 00164 port = 0; 00165 } 00166 00167 // evil, but Glib::Regex is only availabel in ver >= 2.14, n/a on maemo 00168 ipaddr = hostname; 00169 00170 name = hostname; 00171 00172 free(tmpvalue); 00173 return; 00174 } 00175 } 00176 00177 Gtk::TreeModel::iterator iter = treesel->get_selected(); 00178 if (iter) { 00179 Gtk::TreeModel::Row row = *iter; 00180 name = row[__service_model->get_column_record().name]; 00181 hostname = row[__service_model->get_column_record().hostname]; 00182 ipaddr = row[__service_model->get_column_record().ipaddr]; 00183 port = row[__service_model->get_column_record().port]; 00184 00185 } else { 00186 throw Exception("No host selected"); 00187 } 00188 } 00189 00190 00191 /** Get raw address. 00192 * @param addr upon returns contains the raw representation of the IP address 00193 * @param addr_size size in bytes of addr, if addr_size is too small for an 00194 * AF_INET addr an exception is thrown. 00195 */ 00196 void 00197 ServiceChooserDialog::get_raw_address(struct sockaddr *addr, socklen_t addr_size) 00198 { 00199 if ( addr_size < sizeof(struct sockaddr_in) ) { 00200 throw Exception("Size of addrlen too small, only %u bytes, but required %zu\n", 00201 addr_size, sizeof(struct sockaddr_in)); 00202 } 00203 Glib::ustring name, hostname, ipaddr; 00204 unsigned short int port; 00205 get_selected_service (name, hostname, ipaddr, port); 00206 00207 if (inet_pton(AF_INET, ipaddr.c_str(), &(((struct sockaddr_in *)addr)->sin_addr)) <= 0) { 00208 NetworkNameResolver resolver; 00209 struct sockaddr_in *saddr; 00210 socklen_t saddr_len; 00211 if (resolver.resolve_name_blocking(ipaddr.c_str(), (struct sockaddr **)&saddr, &saddr_len)) { 00212 memcpy(addr, saddr, std::min(saddr_len, addr_size)); 00213 } else { 00214 throw Exception("Could not lookup hostname '%s' and it is not a valid IP address", 00215 ipaddr.c_str()); 00216 } 00217 } 00218 00219 } 00220 00221 00222 /** Signal handler for expander event. 00223 * Called when expander is (de-)expanded. Only works with Glibmm properties 00224 * enabled, i.e. not on Maemo. 00225 */ 00226 void 00227 ServiceChooserDialog::on_expander_changed() 00228 { 00229 if (__expander.get_expanded()) { 00230 __entry.grab_focus(); 00231 } else { 00232 __treeview.grab_focus(); 00233 } 00234 } 00235 00236 00237 /** Run dialog and try to connect. 00238 * This runs the service chooser dialog and connects to the given service 00239 * with the attached FawkesNetworkClient. If the connection couldn't be established 00240 * an error dialog is shown. You should not rely on the connection to be 00241 * active after calling this method, rather you should use a ConnectionDispatcher 00242 * to get the "connected" signal. 00243 */ 00244 void 00245 ServiceChooserDialog::run_and_connect() 00246 { 00247 if (! __client) throw NullPointerException("FawkesNetworkClient not set"); 00248 if (__client->connected()) throw Exception("Client is already connected"); 00249 00250 if ( run() ) { 00251 try { 00252 Glib::ustring name; 00253 Glib::ustring hostname; 00254 Glib::ustring ipaddr; 00255 unsigned short int port; 00256 get_selected_service(name, hostname, ipaddr, port); 00257 if ( port == 0 ) port = 1910; 00258 00259 __client->connect(hostname.c_str(), ipaddr.c_str(), port); 00260 } catch (Exception &e) { 00261 Glib::ustring message = *(e.begin()); 00262 Gtk::MessageDialog md(__parent, message, /* markup */ false, 00263 Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, 00264 /* modal */ true); 00265 md.set_title("Connection failed"); 00266 md.run(); 00267 } 00268 } 00269 } 00270 00271 } // end of namespace fawkes