7 from wx.lib.mixins.listctrl import ColumnSorterMixin
8 from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
10 # Classes to interface with a running tinc daemon
18 REQ_DUMP_CONNECTIONS = 6
31 def parse(self, args):
33 self.address = args[2]
35 args.insert(3, 'port')
38 self.cipher = int(args[6])
39 self.digest = int(args[8])
40 self.maclength = int(args[10])
41 self.compression = int(args[12])
42 self.options = int(args[14], 0x10)
43 self.status = int(args[16], 0x10)
44 self.nexthop = args[18]
46 self.distance = int(args[22])
47 self.pmtu = int(args[24])
48 self.minmtu = int(args[26])
49 self.maxmtu = int(args[28][:-1])
54 def parse(self, args):
57 self.address = args[4]
59 self.options = int(args[8], 16)
60 self.weight = int(args[10])
63 def parse(self, args):
64 if args[0].find('#') >= 0:
65 (address, self.weight) = args[0].split('#', 1)
70 if address.find('/') >= 0:
71 (self.address, self.prefixlen) = address.split('/', 1)
73 self.address = address
79 def parse(self, args):
81 self.address = args[2]
83 args.insert(3, 'port')
86 self.options = int(args[6], 0x10)
87 self.socket = int(args[8])
88 self.status = int(args[10], 0x10)
96 f = open(self.pidfile)
97 info = string.split(f.readline())
99 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
100 s.connect((info[2], int(info[4])))
101 self.sf = s.makefile()
103 hello = string.split(self.sf.readline())
105 self.sf.write('0 ^' + info[1] + ' 17\r\n')
107 resp = string.split(self.sf.readline())
112 self.connections = {}
116 self.sf.write('18 3\r\n18 4\r\n18 5\r\n18 6\r\n')
119 for node in self.nodes.values():
121 for edge in self.edges.values():
123 for subnet in self.subnets.values():
124 subnet.visited = False
125 for connections in self.connections.values():
126 connections.visited = False
129 resp = string.split(self.sf.readline())
137 node = self.nodes.get(resp[2]) or Node()
140 self.nodes[resp[2]] = node
144 edge = self.nodes.get((resp[2], resp[4])) or Edge()
147 self.edges[(resp[2], resp[4])] = edge
151 subnet = self.subnets.get((resp[2], resp[4])) or Subnet()
152 subnet.parse(resp[2:])
153 subnet.visited = True
154 self.subnets[(resp[2], resp[4])] = subnet
155 self.nodes[subnet.owner].subnets[resp[2]] = subnet
159 connection = self.connections.get((resp[2], resp[4])) or Connection()
160 connection.parse(resp[2:])
161 connection.visited = True
162 self.connections[(resp[2], resp[4])] = connection
166 for key, subnet in self.subnets.items():
167 if not subnet.visited:
168 del self.subnets[key]
170 for key, edge in self.edges.items():
174 for key, node in self.nodes.items():
178 for key, subnet in node.subnets.items():
179 if not subnet.visited:
180 del node.subnets[key]
182 for key, connection in self.connections.items():
183 if not connection.visited:
184 del self.connections[key]
189 def disconnect(self, name):
190 self.sf.write('18 12 ' + name + '\r\n')
192 resp = string.split(self.sf.readline())
194 def debug(self, level = -1):
195 self.sf.write('18 9 ' + str(level) + '\r\n')
197 resp = string.split(self.sf.readline())
200 def __init__(self, netname = None, pidfile = None):
201 self.tincconf = VPN.confdir + '/'
204 self.netname = netname
205 self.tincconf += netname + '/'
207 self.tincconf += 'tinc.conf'
209 if pidfile is not None:
210 self.pidfile = pidfile
212 self.pidfile = VPN.piddir + 'tinc.'
214 self.pidfile += netname + '.'
215 self.pidfile += 'pid'
224 def usage(exitcode = 0):
225 print('Usage: ' + argv0 + ' [options]')
226 print('\nValid options are:')
227 print(' -n, --net=NETNAME Connect to net NETNAME.')
228 print(' --pidfile=FILENAME Read control cookie from FILENAME.')
229 print(' --help Display this help and exit.')
230 print('\nReport bugs to tinc@tinc-vpn.org.')
234 if sys.argv[0] in ('-n', '--net'):
237 elif sys.argv[0] in ('--pidfile'):
239 pidfile = sys.argv[0]
240 elif sys.argv[0] in ('--help'):
243 print(argv0 + ': unrecognized option \'' + sys.argv[0] + '\'')
248 vpn = VPN(net, pidfile)
251 class SuperListCtrl(wx.ListCtrl, ColumnSorterMixin, ListCtrlAutoWidthMixin):
252 def __init__(self, parent, style):
253 wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
254 ListCtrlAutoWidthMixin.__init__(self)
255 ColumnSorterMixin.__init__(self, 14)
257 def GetListCtrl(self):
261 class SettingsPage(wx.Panel):
262 def OnDebugLevel(self, event):
263 vpn.debug(self.debug.GetValue())
265 def __init__(self, parent, id):
266 wx.Panel.__init__(self, parent, id)
267 grid = wx.FlexGridSizer(cols = 2)
268 grid.AddGrowableCol(0, 1)
270 namelabel = wx.StaticText(self, -1, 'Name:')
271 self.name = wx.TextCtrl(self, -1, vpn.name)
275 portlabel = wx.StaticText(self, -1, 'Port:')
276 self.port = wx.TextCtrl(self, -1, vpn.port)
280 debuglabel = wx.StaticText(self, -1, 'Debug level:')
281 self.debug = wx.SpinCtrl(self, min = 0, max = 5, initial = vpn.debug())
282 self.debug.Bind(wx.EVT_SPINCTRL, self.OnDebugLevel)
286 modelabel = wx.StaticText(self, -1, 'Mode:')
287 self.mode = wx.ComboBox(self, -1, style = wx.CB_READONLY, value = 'Router', choices = ['Router', 'Switch', 'Hub'])
293 class ConnectionsPage(wx.Panel):
294 def __init__(self, parent, id):
295 wx.Panel.__init__(self, parent, id)
296 self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
297 self.list.InsertColumn(0, 'Name')
298 self.list.InsertColumn(1, 'Address')
299 self.list.InsertColumn(2, 'Port')
300 self.list.InsertColumn(3, 'Options')
301 self.list.InsertColumn(4, 'Weight')
303 hbox = wx.BoxSizer(wx.HORIZONTAL)
304 hbox.Add(self.list, 1, wx.EXPAND)
308 class ContextMenu(wx.Menu):
309 def __init__(self, item):
310 wx.Menu.__init__(self)
314 disconnect = wx.MenuItem(self, -1, 'Disconnect')
315 self.AppendItem(disconnect)
316 self.Bind(wx.EVT_MENU, self.OnDisconnect, id=disconnect.GetId())
318 def OnDisconnect(self, event):
319 vpn.disconnect(self.item[0])
321 def OnContext(self, event):
323 self.PopupMenu(self.ContextMenu(self.list.itemDataMap[event.GetIndex()]), event.GetPosition())
326 self.list.itemDataMap = {}
329 for key, connection in vpn.connections.items():
330 if self.list.GetItemCount() <= i:
331 self.list.InsertStringItem(i, connection.name)
333 self.list.SetStringItem(i, 0, connection.name)
334 self.list.SetStringItem(i, 1, connection.address)
335 self.list.SetStringItem(i, 2, connection.port)
336 self.list.SetStringItem(i, 3, str(connection.options))
337 self.list.SetStringItem(i, 4, str(connection.weight))
338 self.list.itemDataMap[i] = (connection.name, connection.address, connection.port, connection.options, connection.weight)
339 self.list.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnContext)
342 while self.list.GetItemCount() > i:
343 self.list.DeleteItem(self.list.GetItemCount() - 1)
346 class NodesPage(wx.Panel):
347 def __init__(self, parent, id):
348 wx.Panel.__init__(self, parent, id)
349 self.list = SuperListCtrl(self, id)
350 self.list.InsertColumn( 0, 'Name')
351 self.list.InsertColumn( 1, 'Address')
352 self.list.InsertColumn( 2, 'Port')
353 self.list.InsertColumn( 3, 'Cipher')
354 self.list.InsertColumn( 4, 'Digest')
355 self.list.InsertColumn( 5, 'MACLength')
356 self.list.InsertColumn( 6, 'Compression')
357 self.list.InsertColumn( 7, 'Options')
358 self.list.InsertColumn( 8, 'Status')
359 self.list.InsertColumn( 9, 'Nexthop')
360 self.list.InsertColumn(10, 'Via')
361 self.list.InsertColumn(11, 'Distance')
362 self.list.InsertColumn(12, 'PMTU')
363 self.list.InsertColumn(13, 'Min MTU')
364 self.list.InsertColumn(14, 'Max MTU')
366 hbox = wx.BoxSizer(wx.HORIZONTAL)
367 hbox.Add(self.list, 1, wx.EXPAND)
372 self.list.itemDataMap = {}
375 for key, node in vpn.nodes.items():
376 if self.list.GetItemCount() <= i:
377 self.list.InsertStringItem(i, node.name)
379 self.list.SetStringItem(i, 0, node.name)
380 self.list.SetStringItem(i, 1, node.address)
381 self.list.SetStringItem(i, 2, node.port)
382 self.list.SetStringItem(i, 3, str(node.cipher))
383 self.list.SetStringItem(i, 4, str(node.digest))
384 self.list.SetStringItem(i, 5, str(node.maclength))
385 self.list.SetStringItem(i, 6, str(node.compression))
386 self.list.SetStringItem(i, 7, str(node.options))
387 self.list.SetStringItem(i, 8, str(node.status))
388 self.list.SetStringItem(i, 9, node.nexthop)
389 self.list.SetStringItem(i, 10, node.via)
390 self.list.SetStringItem(i, 11, str(node.distance))
391 self.list.SetStringItem(i, 12, str(node.pmtu))
392 self.list.SetStringItem(i, 13, str(node.minmtu))
393 self.list.SetStringItem(i, 14, str(node.maxmtu))
394 self.list.itemDataMap[i] = (node.name, node.address, node.port, node.cipher, node.digest, node.maclength, node.compression, node.options, node.status, node.nexthop, node.via, node.distance, node.pmtu, node.minmtu, node.maxmtu)
395 self.list.SetItemData(i, i)
398 while self.list.GetItemCount() > i:
399 self.list.DeleteItem(self.list.GetItemCount() - 1)
401 class EdgesPage(wx.Panel):
402 def __init__(self, parent, id):
403 wx.Panel.__init__(self, parent, id)
404 self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
405 self.list.InsertColumn(0, 'From')
406 self.list.InsertColumn(1, 'To')
407 self.list.InsertColumn(2, 'Address')
408 self.list.InsertColumn(3, 'Port')
409 self.list.InsertColumn(4, 'Options')
410 self.list.InsertColumn(5, 'Weight')
412 hbox = wx.BoxSizer(wx.HORIZONTAL)
413 hbox.Add(self.list, 1, wx.EXPAND)
418 self.list.itemDataMap = {}
421 for key, edge in vpn.edges.items():
422 if self.list.GetItemCount() <= i:
423 self.list.InsertStringItem(i, edge.fr)
425 self.list.SetStringItem(i, 0, edge.fr)
426 self.list.SetStringItem(i, 1, edge.to)
427 self.list.SetStringItem(i, 2, edge.address)
428 self.list.SetStringItem(i, 3, edge.port)
429 self.list.SetStringItem(i, 4, str(edge.options))
430 self.list.SetStringItem(i, 5, str(edge.weight))
431 self.list.itemDataMap[i] = (edge.fr, edge.to, edge.address, edge.port, edge.options, edge.weight)
434 while self.list.GetItemCount() > i:
435 self.list.DeleteItem(self.list.GetItemCount() - 1)
437 class SubnetsPage(wx.Panel):
438 def __init__(self, parent, id):
439 wx.Panel.__init__(self, parent, id)
440 self.list = SuperListCtrl(self, id)
441 self.list.InsertColumn(0, 'Subnet', wx.LIST_FORMAT_RIGHT)
442 self.list.InsertColumn(1, 'Weight', wx.LIST_FORMAT_RIGHT)
443 self.list.InsertColumn(2, 'Owner')
444 hbox = wx.BoxSizer(wx.HORIZONTAL)
445 hbox.Add(self.list, 1, wx.EXPAND)
450 self.list.itemDataMap = {}
453 for key, subnet in vpn.subnets.items():
454 if self.list.GetItemCount() <= i:
455 self.list.InsertStringItem(i, subnet.address + '/' + subnet.prefixlen)
457 self.list.SetStringItem(i, 0, subnet.address + '/' + subnet.prefixlen)
458 self.list.SetStringItem(i, 1, subnet.weight)
459 self.list.SetStringItem(i, 2, subnet.owner)
460 self.list.itemDataMap[i] = (subnet.address + '/' + subnet.prefixlen, subnet.weight, subnet.owner)
463 while self.list.GetItemCount() > i:
464 self.list.DeleteItem(self.list.GetItemCount() - 1)
466 class StatusPage(wx.Panel):
467 def __init__(self, parent, id):
468 wx.Panel.__init__(self, parent, id)
470 class GraphPage(wx.Window):
471 def __init__(self, parent, id):
472 wx.Window.__init__(self, parent, id)
474 class NetPage(wx.Notebook):
475 def __init__(self, parent, id):
476 wx.Notebook.__init__(self, parent)
477 self.settings = SettingsPage(self, id)
478 self.connections = ConnectionsPage(self, id)
479 self.nodes = NodesPage(self, id)
480 self.edges = EdgesPage(self, id)
481 self.subnets = SubnetsPage(self, id)
482 self.graph = GraphPage(self, id)
483 self.status = StatusPage(self, id)
485 self.AddPage(self.settings, 'Settings')
486 #self.AddPage(self.status, 'Status')
487 self.AddPage(self.connections, 'Connections')
488 self.AddPage(self.nodes, 'Nodes')
489 self.AddPage(self.edges, 'Edges')
490 self.AddPage(self.subnets, 'Subnets')
491 #self.AddPage(self.graph, 'Graph')
494 class MainWindow(wx.Frame):
495 def OnQuit(self, event):
498 def OnTimer(self, event):
500 self.np.nodes.refresh()
501 self.np.subnets.refresh()
502 self.np.edges.refresh()
503 self.np.connections.refresh()
505 def __init__(self, parent, id, title):
506 wx.Frame.__init__(self, parent, id, title)
508 menubar = wx.MenuBar()
510 file.Append(1, '&Quit\tCtrl-X', 'Quit tinc GUI')
511 menubar.Append(file, '&File')
513 #nb = wx.Notebook(self, -1)
514 #nb.SetPadding((0, 0))
515 self.np = NetPage(self, -1)
516 #nb.AddPage(np, 'VPN')
518 self.timer = wx.Timer(self, -1)
519 self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
520 self.timer.Start(1000)
521 self.Bind(wx.EVT_MENU, self.OnQuit, id=1)
522 self.SetMenuBar(menubar)
526 mw = MainWindow(None, -1, 'Tinc GUI')
528 #def OnTaskBarIcon(event):
531 #icon = wx.Icon("tincgui.ico", wx.BITMAP_TYPE_PNG)
532 #taskbaricon = wx.TaskBarIcon()
533 #taskbaricon.SetIcon(icon, 'Tinc GUI')
534 #wx.EVT_TASKBAR_RIGHT_UP(taskbaricon, OnTaskBarIcon)