1#!@PYTHON@ 2# -*-python-*- 3# $Id$ 4 5# This file is part of avahi. 6# 7# avahi is free software; you can redistribute it and/or modify it 8# under the terms of the GNU Lesser General Public License as 9# published by the Free Software Foundation; either version 2 of the 10# License, or (at your option) any later version. 11# 12# avahi is distributed in the hope that it will be useful, but WITHOUT 13# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 15# License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public 18# License along with avahi; if not, write to the Free Software 19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 20# USA. 21 22import os, sys 23 24try: 25 import avahi, gettext, gtk, gobject, dbus, avahi.ServiceTypeDatabase 26 from avahi_discover.SimpleGladeApp import SimpleGladeApp 27 gtk.glade.bindtextdomain(@GETTEXT_PACKAGE@, @LOCALEDIR@) 28 gtk.glade.textdomain(@GETTEXT_PACKAGE@) 29 _ = gettext.gettext 30except ImportError, e: 31 print "Sorry, to use this tool you need to install Avahi, pygtk and python-dbus.\n Error: %s" % e 32 sys.exit(1) 33 34 35## !!NOTE!! ## 36# It's really important to do this, else you won't see any events 37## 38try: 39 from dbus import DBusException 40 import dbus.glib 41except ImportError, e: 42 pass 43 44service_type_browsers = {} 45service_browsers = {} 46 47def error_msg(msg): 48 d = gtk.MessageDialog(parent=None, flags=gtk.DIALOG_MODAL, 49 type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK) 50 d.set_markup(msg) 51 d.show_all() 52 d.run() 53 d.destroy() 54 55glade_dir = "@interfacesdir@" 56 57service_type_db = avahi.ServiceTypeDatabase.ServiceTypeDatabase() 58 59class Main_window(SimpleGladeApp): 60 def __init__(self, path="avahi-discover.glade", root="main_window", domain=None, **kwargs): 61 path = os.path.join(glade_dir, path) 62 gtk.window_set_default_icon_name("network-wired") 63 SimpleGladeApp.__init__(self, path, root, domain, **kwargs) 64 65 def on_tree_view_cursor_changed(self, widget, *args): 66 (model, iter) = widget.get_selection().get_selected() 67 stype = None 68 if iter is not None: 69 (name,interface,protocol,stype,domain) = self.treemodel.get(iter,1,2,3,4,5) 70 if stype == None: 71 self.info_label.set_markup("<i>No service currently selected.</i>") 72 return 73 #Asynchronous resolving 74 self.server.ResolveService( int(interface), int(protocol), name, stype, domain, avahi.PROTO_UNSPEC, dbus.UInt32(0), reply_handler=self.service_resolved, error_handler=self.print_error) 75 76 def protoname(self,protocol): 77 if protocol == avahi.PROTO_INET: 78 return "IPv4" 79 if protocol == avahi.PROTO_INET6: 80 return "IPv6" 81 return "n/a" 82 83 def siocgifname(self, interface): 84 if interface <= 0: 85 return "n/a" 86 else: 87 return self.server.GetNetworkInterfaceNameByIndex(interface) 88 89 def get_interface_name(self, interface, protocol): 90 if interface == avahi.IF_UNSPEC and protocol == avahi.PROTO_UNSPEC: 91 return "Wide Area" 92 else: 93 return str(self.siocgifname(interface)) + " " + str(self.protoname(protocol)) 94 95 def service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): 96 print "Service data for service '%s' of type '%s' in domain '%s' on %i.%i:" % (name, stype, domain, interface, protocol) 97 98 print "\tHost %s (%s), port %i, TXT data: %s" % (host, address, port, str(avahi.txt_array_to_string_array(txt))) 99 100 self.update_label(interface, protocol, name, stype, domain, host, aprotocol, address, port, avahi.txt_array_to_string_array(txt)) 101 102 def print_error(self, err): 103 error_label = "<b>Error:</b> %s" % (err) 104 self.info_label.set_markup(error_label) 105 print "Error:", str(err) 106 107 def lookup_type(self, stype): 108 global service_type_db 109 110 try: 111 return service_type_db[stype] 112 except KeyError: 113 return stype 114 115 def new_service(self, interface, protocol, name, stype, domain, flags): 116 print "Found service '%s' of type '%s' in domain '%s' on %i.%i." % (name, stype, domain, interface, protocol) 117 if self.zc_ifaces.has_key((interface,protocol)) == False: 118 119 ifn = self.get_interface_name(interface, protocol) 120 121 self.zc_ifaces[(interface,protocol)] = self.insert_row(self.treemodel, None, ifn, None,interface,protocol,None,domain) 122 if self.zc_domains.has_key((interface,protocol,domain)) == False: 123 self.zc_domains[(interface,protocol,domain)] = self.insert_row(self.treemodel, self.zc_ifaces[(interface,protocol)], domain,None,interface,protocol,None,domain) 124 if self.zc_types.has_key((interface,protocol,stype,domain)) == False: 125 thisDomain = self.zc_domains[(interface,protocol,domain)] 126 self.zc_types[(interface,protocol,stype,domain)] = self.insert_row(self.treemodel, thisDomain, self.lookup_type(stype), name, interface,None,None,None) 127 treeiter = self.insert_row(self.treemodel,self.zc_types[(interface,protocol,stype,domain)], name, name, interface,protocol,stype,domain) 128 self.services_browsed[(interface, protocol, name, stype, domain)] = treeiter 129 # expand the tree of this path 130 self.tree_view.expand_to_path(self.treemodel.get_path(treeiter)) 131 132 def remove_service(self, interface, protocol, name, stype, domain, flags): 133 print "Service '%s' of type '%s' in domain '%s' on %i.%i disappeared." % (name, stype, domain, interface, protocol) 134 self.info_label.set_markup("") 135 treeiter=self.services_browsed[(interface, protocol, name, stype, domain)] 136 parent = self.treemodel.iter_parent(treeiter) 137 self.treemodel.remove(treeiter) 138 del self.services_browsed[(interface, protocol, name, stype, domain)] 139 if self.treemodel.iter_has_child(parent) == False: 140 treeiter=self.zc_types[(interface,protocol,stype,domain)] 141 parent = self.treemodel.iter_parent(treeiter) 142 self.treemodel.remove(treeiter) 143 del self.zc_types[(interface,protocol,stype,domain)] 144 if self.treemodel.iter_has_child(parent) == False: 145 treeiter=self.zc_domains[(interface,protocol,domain)] 146 parent = self.treemodel.iter_parent(treeiter) 147 self.treemodel.remove(treeiter) 148 del self.zc_domains[(interface,protocol,domain)] 149 if self.treemodel.iter_has_child(parent) == False: 150 treeiter=self.zc_ifaces[(interface,protocol)] 151 parent = self.treemodel.iter_parent(treeiter) 152 self.treemodel.remove(treeiter) 153 del self.zc_ifaces[(interface,protocol)] 154 155 def new_service_type(self, interface, protocol, stype, domain, flags): 156 global service_browsers 157 158 # Are we already browsing this domain for this type? 159 if service_browsers.has_key((interface, protocol, stype, domain)): 160 return 161 162 print "Browsing for services of type '%s' in domain '%s' on %i.%i ..." % (stype, domain, interface, protocol) 163 164 b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceBrowserNew(interface, protocol, stype, domain, dbus.UInt32(0))), avahi.DBUS_INTERFACE_SERVICE_BROWSER) 165 b.connect_to_signal('ItemNew', self.new_service) 166 b.connect_to_signal('ItemRemove', self.remove_service) 167 168 service_browsers[(interface, protocol, stype, domain)] = b 169 170 def browse_domain(self, interface, protocol, domain): 171 global service_type_browsers 172 173 # Are we already browsing this domain? 174 if service_type_browsers.has_key((interface, protocol, domain)): 175 return 176 177 if self.stype is None: 178 print "Browsing domain '%s' on %i.%i ..." % (domain, interface, protocol) 179 180 try: 181 b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0))), avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER) 182 except DBusException, e: 183 print e 184 error_msg("You should check that the avahi daemon is running.\n\nError : %s" % e) 185 sys.exit(0) 186 187 b.connect_to_signal('ItemNew', self.new_service_type) 188 189 service_type_browsers[(interface, protocol, domain)] = b 190 else: 191 new_service_type(interface, protocol, stype, domain) 192 193 def new_domain(self,interface, protocol, domain, flags): 194 if self.zc_ifaces.has_key((interface,protocol)) == False: 195 ifn = self.get_interface_name(interface, protocol) 196 self.zc_ifaces[(interface,protocol)] = self.insert_row(self.treemodel, None, ifn,None,interface,protocol,None,domain) 197 if self.zc_domains.has_key((interface,protocol,domain)) == False: 198 self.zc_domains[(interface,protocol,domain)] = self.insert_row(self.treemodel, self.zc_ifaces[(interface,protocol)], domain,None,interface,protocol,None,domain) 199 if domain != "local": 200 self.browse_domain(interface, protocol, domain) 201 202 def pair_to_dict(self, l): 203 res = dict() 204 for el in l: 205 if "=" not in el: 206 res[el]='' 207 else: 208 tmp = el.split('=',1) 209 if len(tmp[0]) > 0: 210 res[tmp[0]] = tmp[1] 211 return res 212 213 214 def update_label(self,interface, protocol, name, stype, domain, host, aprotocol, address, port, txt): 215 if len(txt) != 0: 216 txts = "" 217 txtd = self.pair_to_dict(txt) 218 for k,v in txtd.items(): 219 txts+="<b>TXT <i>%s</i></b> = %s\n" % (k,v) 220 else: 221 txts = "<b>TXT Data:</b> <i>empty</i>" 222 223 infos = "<b>Service Type:</b> %s\n<b>Service Name:</b> %s\n<b>Domain Name:</b> %s\n<b>Interface:</b> %s %s\n<b>Address:</b> %s/%s:%i\n%s" % (stype, name, domain, self.siocgifname(interface), self.protoname(protocol), host, address, port, txts.strip()) 224 self.info_label.set_markup(infos) 225 226 def insert_row(self, model,parent, 227 content, name, interface,protocol,stype,domain): 228 myiter=model.insert_after(parent,None) 229 model.set(myiter,0,content,1,name,2,interface,3,protocol,4,stype,5,domain) 230 return myiter 231 232 def new(self): 233 print "A new main_window has been created" 234 self.treemodel=gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) 235 self.tree_view.set_model(self.treemodel) 236 237 #creating the columns headers 238 self.tree_view.set_headers_visible(False) 239 renderer=gtk.CellRendererText() 240 column=gtk.TreeViewColumn("",renderer, text=0) 241 column.set_resizable(True) 242 column.set_sizing("GTK_TREE_VIEW_COLUMN_GROW_ONLY"); 243 column.set_expand(True); 244 self.tree_view.append_column(column) 245 246 self.domain = None 247 self.stype = None 248 self.zc_ifaces = {} 249 self.zc_domains = {} 250 self.zc_types = {} 251 self.services_browsed = {} 252 253 self.bus = dbus.SystemBus() 254 self.server = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) 255 256 if self.domain is None: 257 # Explicitly browse .local 258 self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "local") 259 260 # Browse for other browsable domains 261 db = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.DomainBrowserNew(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "", avahi.DOMAIN_BROWSER_BROWSE, dbus.UInt32(0))), avahi.DBUS_INTERFACE_DOMAIN_BROWSER) 262 db.connect_to_signal('ItemNew', self.new_domain) 263 else: 264 # Just browse the domain the user wants us to browse 265 self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, domain) 266 267 268def main(): 269 main_window = Main_window() 270 271 main_window.run() 272 273if __name__ == "__main__": 274 main() 275 276 277