#!/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 <Name>.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()
