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
35 print('Deleting node ' + self.name)
37 def parse(self, args):
39 self.address = args[2]
41 args.insert(3, 'port')
44 self.cipher = int(args[6])
45 self.digest = int(args[8])
46 self.maclength = int(args[10])
47 self.compression = int(args[12])
48 self.options = int(args[14], 0x10)
49 self.status = int(args[16], 0x10)
50 self.nexthop = args[18]
52 self.distance = int(args[22])
53 self.pmtu = int(args[24])
54 self.minmtu = int(args[26])
55 self.maxmtu = int(args[28][:-1])
60 def parse(self, args):
63 self.address = args[4]
65 self.options = int(args[8], 16)
66 self.weight = int(args[10])
69 def parse(self, args):
70 if args[0].find('#') >= 0:
71 (address, self.weight) = args[0].split('#', 1)
76 if address.find('/') >= 0:
77 (self.address, self.prefixlen) = address.split('/', 1)
79 self.address = address
85 def parse(self, args):
87 self.address = args[2]
89 args.insert(3, 'port')
92 self.options = int(args[6], 0x10)
93 self.socket = int(args[8])
94 self.status = int(args[10], 0x10)
102 f = open(self.pidfile)
103 info = string.split(f.readline())
105 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
106 s.connect((info[2], int(info[4])))
107 self.sf = s.makefile()
109 hello = string.split(self.sf.readline())
111 self.sf.write('0 ^' + info[1] + ' 17\r\n')
113 resp = string.split(self.sf.readline())
118 self.connections = {}
122 self.sf.write('18 3\r\n18 4\r\n18 5\r\n18 6\r\n')
125 for node in self.nodes.values():
127 for edge in self.edges.values():
129 for subnet in self.subnets.values():
130 subnet.visited = False
131 for connections in self.connections.values():
132 connections.visited = False
135 resp = string.split(self.sf.readline())
143 node = self.nodes.get(resp[2]) or Node()
146 self.nodes[resp[2]] = node
150 edge = self.nodes.get((resp[2], resp[4])) or Edge()
153 self.edges[(resp[2], resp[4])] = edge
157 subnet = self.subnets.get((resp[2], resp[4])) or Subnet()
158 subnet.parse(resp[2:])
159 subnet.visited = True
160 self.subnets[(resp[2], resp[4])] = subnet
161 self.nodes[subnet.owner].subnets[resp[2]] = subnet
165 connection = self.connections.get((resp[2], resp[4])) or Connection()
166 connection.parse(resp[2:])
167 connection.visited = True
168 self.connections[(resp[2], resp[4])] = connection
172 for key, subnet in self.subnets.items():
173 if not subnet.visited:
174 del self.subnets[key]
176 for key, edge in self.edges.items():
180 for key, node in self.nodes.items():
184 for key, subnet in node.subnets.items():
185 if not subnet.visited:
186 del node.subnets[key]
188 for key, connection in self.connections.items():
189 if not connection.visited:
190 del self.connections[key]
195 def disconnect(self, name):
196 self.sf.write('18 12 ' + name + '\r\n')
198 resp = string.split(self.sf.readline())
200 def debug(self, level = -1):
201 self.sf.write('18 9 ' + str(level) + '\r\n')
203 resp = string.split(self.sf.readline())
206 def __init__(self, netname = None, pidfile = None):
207 self.tincconf = VPN.confdir + '/'
210 self.netname = netname
211 self.tincconf += netname + '/'
213 self.tincconf += 'tinc.conf'
215 if pidfile is not None:
216 self.pidfile = pidfile
218 self.pidfile = VPN.piddir + 'tinc.'
220 self.pidfile += netname + '.'
221 self.pidfile += 'pid'
230 def usage(exitcode = 0):
231 print('Usage: ' + argv0 + ' [options]')
232 print('\nValid options are:')
233 print(' -n, --net=NETNAME Connect to net NETNAME.')
234 print(' --pidfile=FILENAME Read control cookie from FILENAME.')
235 print(' --help Display this help and exit.')
236 print('\nReport bugs to tinc@tinc-vpn.org.')
240 if sys.argv[0] in ('-n', '--net'):
243 elif sys.argv[0] in ('--pidfile'):
245 pidfile = sys.argv[0]
246 elif sys.argv[0] in ('--help'):
249 print(argv0 + ': unrecognized option \'' + sys.argv[0] + '\'')
254 vpn = VPN(net, pidfile)
257 class SuperListCtrl(wx.ListCtrl, ColumnSorterMixin, ListCtrlAutoWidthMixin):
258 def __init__(self, parent, style):
259 wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
260 ListCtrlAutoWidthMixin.__init__(self)
261 ColumnSorterMixin.__init__(self, 14)
263 def GetListCtrl(self):
267 class SettingsPage(wx.Panel):
268 def OnDebugLevel(self, event):
269 vpn.debug(self.debug.GetValue())
271 def __init__(self, parent, id):
272 wx.Panel.__init__(self, parent, id)
273 grid = wx.FlexGridSizer(cols = 2)
274 grid.AddGrowableCol(0, 1)
276 namelabel = wx.StaticText(self, -1, 'Name:')
277 self.name = wx.TextCtrl(self, -1, vpn.name)
281 portlabel = wx.StaticText(self, -1, 'Port:')
282 self.port = wx.TextCtrl(self, -1, vpn.port)
286 debuglabel = wx.StaticText(self, -1, 'Debug level:')
287 self.debug = wx.SpinCtrl(self, min = 0, max = 5, initial = vpn.debug())
288 self.debug.Bind(wx.EVT_SPINCTRL, self.OnDebugLevel)
292 modelabel = wx.StaticText(self, -1, 'Mode:')
293 self.mode = wx.ComboBox(self, -1, style = wx.CB_READONLY, value = 'Router', choices = ['Router', 'Switch', 'Hub'])
299 class ConnectionsPage(wx.Panel):
300 def __init__(self, parent, id):
301 wx.Panel.__init__(self, parent, id)
302 self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
303 self.list.InsertColumn(0, 'Name')
304 self.list.InsertColumn(1, 'Address')
305 self.list.InsertColumn(2, 'Port')
306 self.list.InsertColumn(3, 'Options')
307 self.list.InsertColumn(4, 'Weight')
309 hbox = wx.BoxSizer(wx.HORIZONTAL)
310 hbox.Add(self.list, 1, wx.EXPAND)
314 class ContextMenu(wx.Menu):
315 def __init__(self, item):
316 wx.Menu.__init__(self)
320 disconnect = wx.MenuItem(self, -1, 'Disconnect')
321 self.AppendItem(disconnect)
322 self.Bind(wx.EVT_MENU, self.OnDisconnect, id=disconnect.GetId())
324 def OnDisconnect(self, event):
325 print('Disconnecting ' + self.item[0])
326 vpn.disconnect(self.item[0])
328 def OnContext(self, event):
329 print('Context menu!')
332 self.PopupMenu(self.ContextMenu(self.list.itemDataMap[event.GetIndex()]), event.GetPosition())
335 self.list.itemDataMap = {}
338 for key, connection in vpn.connections.items():
339 if self.list.GetItemCount() <= i:
340 self.list.InsertStringItem(i, connection.name)
342 self.list.SetStringItem(i, 0, connection.name)
343 self.list.SetStringItem(i, 1, connection.address)
344 self.list.SetStringItem(i, 2, connection.port)
345 self.list.SetStringItem(i, 3, str(connection.options))
346 self.list.SetStringItem(i, 4, str(connection.weight))
347 self.list.itemDataMap[i] = (connection.name, connection.address, connection.port, connection.options, connection.weight)
348 self.list.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnContext)
351 while self.list.GetItemCount() > i:
352 self.list.DeleteItem(self.list.GetItemCount() - 1)
355 class NodesPage(wx.Panel):
356 def __init__(self, parent, id):
357 wx.Panel.__init__(self, parent, id)
358 self.list = SuperListCtrl(self, id)
359 self.list.InsertColumn( 0, 'Name')
360 self.list.InsertColumn( 1, 'Address')
361 self.list.InsertColumn( 2, 'Port')
362 self.list.InsertColumn( 3, 'Cipher')
363 self.list.InsertColumn( 4, 'Digest')
364 self.list.InsertColumn( 5, 'MACLength')
365 self.list.InsertColumn( 6, 'Compression')
366 self.list.InsertColumn( 7, 'Options')
367 self.list.InsertColumn( 8, 'Status')
368 self.list.InsertColumn( 9, 'Nexthop')
369 self.list.InsertColumn(10, 'Via')
370 self.list.InsertColumn(11, 'Distance')
371 self.list.InsertColumn(12, 'PMTU')
372 self.list.InsertColumn(13, 'Min MTU')
373 self.list.InsertColumn(14, 'Max MTU')
375 hbox = wx.BoxSizer(wx.HORIZONTAL)
376 hbox.Add(self.list, 1, wx.EXPAND)
381 self.list.itemDataMap = {}
384 for key, node in vpn.nodes.items():
385 if self.list.GetItemCount() <= i:
386 self.list.InsertStringItem(i, node.name)
388 self.list.SetStringItem(i, 0, node.name)
389 self.list.SetStringItem(i, 1, node.address)
390 self.list.SetStringItem(i, 2, node.port)
391 self.list.SetStringItem(i, 3, str(node.cipher))
392 self.list.SetStringItem(i, 4, str(node.digest))
393 self.list.SetStringItem(i, 5, str(node.maclength))
394 self.list.SetStringItem(i, 6, str(node.compression))
395 self.list.SetStringItem(i, 7, str(node.options))
396 self.list.SetStringItem(i, 8, str(node.status))
397 self.list.SetStringItem(i, 9, node.nexthop)
398 self.list.SetStringItem(i, 10, node.via)
399 self.list.SetStringItem(i, 11, str(node.distance))
400 self.list.SetStringItem(i, 12, str(node.pmtu))
401 self.list.SetStringItem(i, 13, str(node.minmtu))
402 self.list.SetStringItem(i, 14, str(node.maxmtu))
403 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)
404 self.list.SetItemData(i, i)
407 while self.list.GetItemCount() > i:
408 self.list.DeleteItem(self.list.GetItemCount() - 1)
410 class EdgesPage(wx.Panel):
411 def __init__(self, parent, id):
412 wx.Panel.__init__(self, parent, id)
413 self.list = wx.ListCtrl(self, id, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES)
414 self.list.InsertColumn(0, 'From')
415 self.list.InsertColumn(1, 'To')
416 self.list.InsertColumn(2, 'Address')
417 self.list.InsertColumn(3, 'Port')
418 self.list.InsertColumn(4, 'Options')
419 self.list.InsertColumn(5, 'Weight')
421 hbox = wx.BoxSizer(wx.HORIZONTAL)
422 hbox.Add(self.list, 1, wx.EXPAND)
427 self.list.itemDataMap = {}
430 for key, edge in vpn.edges.items():
431 if self.list.GetItemCount() <= i:
432 self.list.InsertStringItem(i, edge.fr)
434 self.list.SetStringItem(i, 0, edge.fr)
435 self.list.SetStringItem(i, 1, edge.to)
436 self.list.SetStringItem(i, 2, edge.address)
437 self.list.SetStringItem(i, 3, edge.port)
438 self.list.SetStringItem(i, 4, str(edge.options))
439 self.list.SetStringItem(i, 5, str(edge.weight))
440 self.list.itemDataMap[i] = (edge.fr, edge.to, edge.address, edge.port, edge.options, edge.weight)
443 while self.list.GetItemCount() > i:
444 self.list.DeleteItem(self.list.GetItemCount() - 1)
446 class SubnetsPage(wx.Panel):
447 def __init__(self, parent, id):
448 wx.Panel.__init__(self, parent, id)
449 self.list = SuperListCtrl(self, id)
450 self.list.InsertColumn(0, 'Subnet', wx.LIST_FORMAT_RIGHT)
451 self.list.InsertColumn(1, 'Weight', wx.LIST_FORMAT_RIGHT)
452 self.list.InsertColumn(2, 'Owner')
453 hbox = wx.BoxSizer(wx.HORIZONTAL)
454 hbox.Add(self.list, 1, wx.EXPAND)
459 self.list.itemDataMap = {}
462 for key, subnet in vpn.subnets.items():
463 if self.list.GetItemCount() <= i:
464 self.list.InsertStringItem(i, subnet.address + '/' + subnet.prefixlen)
466 self.list.SetStringItem(i, 0, subnet.address + '/' + subnet.prefixlen)
467 self.list.SetStringItem(i, 1, subnet.weight)
468 self.list.SetStringItem(i, 2, subnet.owner)
469 self.list.itemDataMap[i] = (subnet.address + '/' + subnet.prefixlen, subnet.weight, subnet.owner)
472 while self.list.GetItemCount() > i:
473 self.list.DeleteItem(self.list.GetItemCount() - 1)
475 class StatusPage(wx.Panel):
476 def __init__(self, parent, id):
477 wx.Panel.__init__(self, parent, id)
479 class GraphPage(wx.Window):
480 def __init__(self, parent, id):
481 wx.Window.__init__(self, parent, id)
483 class NetPage(wx.Notebook):
484 def __init__(self, parent, id):
485 wx.Notebook.__init__(self, parent)
486 self.settings = SettingsPage(self, id)
487 self.connections = ConnectionsPage(self, id)
488 self.nodes = NodesPage(self, id)
489 self.edges = EdgesPage(self, id)
490 self.subnets = SubnetsPage(self, id)
491 self.graph = GraphPage(self, id)
492 self.status = StatusPage(self, id)
494 self.AddPage(self.settings, 'Settings')
495 #self.AddPage(self.status, 'Status')
496 self.AddPage(self.connections, 'Connections')
497 self.AddPage(self.nodes, 'Nodes')
498 self.AddPage(self.edges, 'Edges')
499 self.AddPage(self.subnets, 'Subnets')
500 #self.AddPage(self.graph, 'Graph')
503 class MainWindow(wx.Frame):
504 def OnQuit(self, event):
507 def OnTimer(self, event):
509 self.np.nodes.refresh()
510 self.np.subnets.refresh()
511 self.np.edges.refresh()
512 self.np.connections.refresh()
514 def __init__(self, parent, id, title):
515 wx.Frame.__init__(self, parent, id, title)
517 menubar = wx.MenuBar()
519 file.Append(1, '&Quit\tCtrl-X', 'Quit tinc GUI')
520 menubar.Append(file, '&File')
522 #nb = wx.Notebook(self, -1)
523 #nb.SetPadding((0, 0))
524 self.np = NetPage(self, -1)
525 #nb.AddPage(np, 'VPN')
527 self.timer = wx.Timer(self, -1)
528 self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
529 self.timer.Start(1000)
530 self.Bind(wx.EVT_MENU, self.OnQuit, id=1)
531 self.SetMenuBar(menubar)
535 mw = MainWindow(None, -1, 'Tinc GUI')
537 #def OnTaskBarIcon(event):
540 #icon = wx.Icon("tincgui.ico", wx.BITMAP_TYPE_PNG)
541 #taskbaricon = wx.TaskBarIcon()
542 #taskbaricon.SetIcon(icon, 'Tinc GUI')
543 #wx.EVT_TASKBAR_RIGHT_UP(taskbaricon, OnTaskBarIcon)