#!/usr/bin/env python # # $Id: pptpconfig.py,v 1.14 2005/05/19 22:53:16 quozl Exp $ # # pptpconfig.py, PPTP configuration and management GUI # Copyright (C) 2002-2005 James Cameron (quozl@us.netrek.org) # [add further copyright owners here] # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # 2005-05-17 22:35 # test harness to instantiate user interface, allow start button to # open tunnel window, and test use of popen() to run a ping command # with output to text view widget. import sys import os import fcntl import signal import pygtk pygtk.require('2.0') import gobject # allow control/c in console to terminate us signal.signal(signal.SIGINT, signal.SIG_DFL) def program_id(): """ string set by CVS on commit """ cvs_id = '$Id: pptpconfig.py,v 1.14 2005/05/19 22:53:16 quozl Exp $'.split() return ' '.join([ os.path.basename(sys.argv[0]), cvs_id[2], cvs_id[3], cvs_id[4]]) class Command: """ run a command in a pipe, sending output to caller's scribe method """ def __init__(self, obj, cmd): obj.scribe(cmd + '\n') p = os.popen(cmd + ' 2>&1', 'r') fcntl.fcntl(p.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) gobject.io_add_watch(p, gobject.IO_IN, self.reader, obj) gobject.io_add_watch(p, gobject.IO_HUP, self.closer, obj) # TODO: this doesn't quite work yet, the last line of the ping # output does not appear, and the program busy-loops somewhere. def reader(self, p, cb_condition, obj): print "reader" print p print cb_condition text = p.readline() # TODO: how to detect EOF on pipe? # use .read() as workaround for missing last line of ping's output obj.scribe(text) print text return 1 def closer(self, p, cb_condition, obj): stat = p.close() reas = 'unknown' sig = None if stat: if os.WIFEXITED(stat): reas = 'exit status' sig = os.WEXITSTATUS(stat) if os.WIFSIGNALED(stat): reas = 'terminated by signal' sig = os.WTERMSIG(stat) if os.WIFSTOPPED(stat): reas = 'stopped by signal' sig = os.WSTOPSIG(stat) obj.scribe(reas) print reas print sig return 0 class Bar: def __init__(self, statusbar): self.statusbar = statusbar self.context = statusbar.get_context_id('text') def show(self, message): self.statusbar.pop(self.context) self.statusbar.push(self.context, message) class Tunnel: def __init__(self): self.xml = gtk.glade.XML('pptpconfig.glade', 'pptpconfig-tunnel') self.xml.signal_autoconnect(self) self.top = self.xml.get_widget('pptpconfig-tunnel') self.textview = self.xml.get_widget('text') self.textbuffer = gtk.TextBuffer(None) self.textview.set_buffer(self.textbuffer) # TODO: remove following line after testing self.textbuffer.insert_at_cursor('hello\n', 6) self.eof = self.textbuffer.create_mark("end", self.textbuffer.get_end_iter(), False) def show(self): self.top.show() def hide(self): self.top.hide() def scribe(self, str): # TODO: colour highlighting port self.textbuffer.insert_at_cursor(str, len(str)) self.textview.scroll_to_mark(self.eof, 0.05, True, 0.0, 1.0) def on_tunnel_ping_clicked(self, obj): # temporary ping test Command(self, 'ping -c 5 10.0.0.5') def on_tunnel_close_activate(self, obj): self.top.hide() def on_tunnel_close_clicked(self, obj): self.top.hide() def on_tunnel_delete_event(self, obj, ign): self.top.hide() return 1 # TODO: rest of callbacks class Setup: """ Setup class """ def __init__(self): self.xml = gtk.glade.XML('pptpconfig.glade', 'pptpconfig-setup') self.xml.signal_autoconnect(self) self.bar = Bar(self.xml.get_widget('statusbar')) self.bar.show('Welcome to ' + program_id()) self.tunnel = None # TODO: read previously defined tunnels from /etc/pptp and # display them in the list. # TODO: find Python implementation of PHP serialize, use on # read so as to provide compatibility with PHP implementation, # but decide on some human readable Python readable format on # write def on_setup_delete_event(self, obj, ign): """ delete event """ gtk.main_quit() return 0 def on_setup_select_row(self): """ select row """ print 'unimplemented' def on_setup_unselect_row(self): """ unselect row """ print 'unimplemented' def on_setup_press_row(self): """ press row """ print 'unimplemented' def on_setup_add_clicked(self, obj): """ add new tunnel: validate user's input, insist on mandatory fields, add per tunnel files .pptp in /etc/ppp/peers/ warn if file exists to use Update to overwrite """ self.name = gtk.Entry() self.name = self.xml.get_widget('name') remote_name = self.name.get_text() # temporary gtk entry test print "Entry contents: %s\n" % remote_name #if remote_name == '': try: tfile = open('/tmp/tunnel.pptp', 'w') except IOError: # TODO: complete gtk Dialog print 'can not open file' dialog = gtk.Dialog("Message", None, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) dialog.show() #dialog.add_action_widget(dialog, self) else: tfile.write('remotename ') tfile.write(remote_name) tfile.write('\n\n') tfile.close() def on_setup_update_clicked(self): """ update existing tunnel: call on_setup_row_select() to select the tunnel file truncate existing file and put new values again """ print 'unimplemented' def on_setup_delete_clicked(self): """ delete existing tunnel: call on_setup_row_select() to select the tunnel file delete the file """ print 'unimplemented' def on_setup_start_clicked(self, obj): """ start clicked """ # TODO: support more than one active tunnel if not self.tunnel: self.tunnel = Tunnel() else: self.tunnel.show() def on_setup_stop_clicked(self, obj): """ stop clicked """ print 'unimplemented' def on_setup_close_clicked(self, obj): """ close clicked """ gtk.main_quit() def on_setup_popup_start_activate(self, obj): """ popup start activate """ print 'unimplemented' def on_setup_popup_stop_activate(self, obj): """ popup stop activate """ print 'unimplemented' # TODO: rest of callbacks if __name__ == '__main__': try: import gtk import gtk.glade Setup() gtk.main() except RuntimeError: print 'Error in', program_id() import traceback traceback.print_exc()