Below is the file 'tftp-console.py' from this revision. You can also download the file.
#!/usr/bin/env python # pygtk stuff import pygtk pygtk.require("2.0") import gtk import gtk.glade import gobject import os import sys import socket import gtktftp class TFTPDialog: def __init__(self): port = 69 try: self.tftp = gtktftp.gTFTPServer(port) except socket.error: self.display_error("Unable to bind to the TFTP port (%d/UDP)" % port, """\ Some platforms require elevated privileges to bind to low ports. Consult your platform documentation and the README file that came with this software. For your reference, you are on the '%s' platform.""" % sys.platform) raise except: self.display_error("Unable to initialise TFTP server", """\ This is almost certainly a bug. Please contact the author: Grahame Bowland <grahame@angrygoats.net> Include as much information as possible. Please also let him know you are on the '%s' platform.""" % sys.platform) raise self.tftp_signals = [] self.create_tftp_signals() # load the glade file; grab widget references xml = open("tftp-console.glade").read() self.glade = gtk.glade.xml_new_from_buffer(xml, len(xml)) self.window = self.glade.get_widget("main_window") self.window.connect("destroy", self.window_destroy) self.execute_button = self.glade.get_widget("execute_button") self.execute_button.connect("clicked", self.execute_button_clicked) self.add_button = self.glade.get_widget("add_button") self.add_button.connect("clicked", self.add_button_clicked) self.remove_button = self.glade.get_widget("remove_button") self.remove_button.connect("clicked", self.remove_button_clicked) self.status_bar = self.glade.get_widget("status_bar") self.tree_view = self.glade.get_widget("file_tree_view") quit_menu = self.glade.get_widget("quit1") about_menu = self.glade.get_widget("about1") # set up the list store self.list_store = gtk.ListStore(str, str) self.tree_view.set_model(self.list_store) # filename column self.filename_column = gtk.TreeViewColumn("Filename") self.tree_view.append_column(self.filename_column) self.filename_cell = gtk.CellRendererText() self.filename_column.pack_start(self.filename_cell, True) self.filename_column.set_attributes(self.filename_cell, text=0) # size column self.size_column = gtk.TreeViewColumn("Size") self.tree_view.append_column(self.size_column) self.size_cell = gtk.CellRendererText() self.size_column.pack_start(self.size_cell, True) self.size_column.set_attributes(self.size_cell, text=1) # callbacks treeselection = self.tree_view.get_selection() treeselection.connect("changed", self.tree_view_update) quit_menu.connect("activate", self.window_destroy) about_menu.connect("activate", self.about) # initialise list buttons self.remove_button.set_sensitive(False) self.window.show_all() def create_tftp_signals(self): # hook up the TFTP server to our UI events to_connect = [("data-received", self.tftp_data_received), ("data-sent", self.tftp_data_sent), ("file-received", self.tftp_file_received), ("file-sent", self.tftp_file_sent), ("error-received", self.tftp_error_received), ("error-sent", self.tftp_error_sent)] self.tftp_signals += map(lambda x: self.tftp.connect(x[0], x[1]), to_connect) def disconnect_tftp_signals(self): map(self.tftp.disconnect, self.tftp_signals) def _add_file(self, filename): data = open(filename, "rb").read() length = len(data) if length < 1024: length = str(length) + " bytes" elif length < 1048576: length = str(length / 1024) + " kilobytes" else: length = str(length / 1048576) + " megabytes" basename = os.path.basename(filename) if self.tftp.tftp.store.has_export(basename): dialog = gtk.Dialog(title="Replace file?", parent=None, flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_NO,gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES)) label = gtk.Label('<span size="large" weight="bold">There is already a file named "%s" in the exported files list.</span>\n\nDo you wish to replace it?' % (basename)) label.set_use_markup(True) dialog.set_has_separator(False) dialog.set_border_width(12) dialog.vbox.pack_start(label, True, True, 0) label.show() response = dialog.run() dialog.destroy() if response != gtk.RESPONSE_YES: return False else: iter = self.list_store.append([basename, length]) self.tftp.tftp.store.set_export(basename, data) return True def display_error(self, description, explanation): dialog = gtk.Dialog(title="Error", parent=None, flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) label = gtk.Label('<span size="large" weight="bold">%s</span>\n\n%s' % (description, explanation)) label.set_use_markup(True) dialog.set_has_separator(False) dialog.set_border_width(12) dialog.vbox.pack_start(label, True, True, 0) label.show() response = dialog.run() dialog.destroy() def update_bar(self, str): self.status_bar.pop(1) self.status_bar.push(1, str) def tftp_data_received(self, widget, address, filename, bytes): self.update_bar("%s: Receiving %s (%d bytes)" % (address, filename, bytes)) def tftp_data_sent(self, widget, address, filename, bytes): self.update_bar("%s: Sending %s (%d bytes)" % (address, filename, bytes)) def tftp_file_received(self, widget, address, filename): self.update_bar("%s: Sucessfully received %s" % (address, filename)) filechooser = gtk.FileChooserDialog(title="Save received file", \ action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) filechooser.set_current_name(filename) response = filechooser.run() if response == gtk.RESPONSE_OK: save_to = filechooser.get_filename() open(save_to, "w").write(self.tftp.tftp.store.get_received(filename)) filechooser.destroy() self.tftp.tftp.store.clear_received(filename) def tftp_file_sent(self, widget, address, filename): self.update_bar("%s: Sucessfully sent %s" % (address, filename)) def tftp_error_received(self, widget, address, code, mesg): self.update_bar("%s: Error received: %s (%s)" % (address, mesg, code)) def tftp_error_sent(self, widget, address, code, mesg): self.update_bar("%s: Error sent: %s (%s)" % (address, mesg, code)) def new_tftp_connection(self, widget, user=None): pass def window_destroy(self, widget, user=None): gtk.main_quit() def about(self, widget, user=None): about = gtk.AboutDialog() about.set_name("TFTP Console") about.set_version("0.1") about.set_copyright("Copyright 2005 Grahame Bowland") about.set_website("http://grahame.angrygoats.net/") about.set_authors(["Grahame Bowland"]) about.set_license("""\ TFTP Console Copyright (C) 2005 Grahame Bowland 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 """) about.run() about.destroy() def add_button_clicked(self, widget, user=None): filechooser = gtk.FileChooserDialog(title="Select files to export", \ action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) filechooser.set_select_multiple(True) response = filechooser.run() if response == gtk.RESPONSE_OK: filenames = filechooser.get_filenames() map(self._add_file, filenames) filechooser.destroy() def remove_button_clicked(self, widget, user=None): treeselection = self.tree_view.get_selection() (model, iter) = treeselection.get_selected() if iter: filename = self.list_store.get_value(iter, 0) self.list_store.remove(iter) self.tftp.tftp.store.clear_export(filename) self.tree_view_update() def tree_view_update(self, iter=None): treeselection = self.tree_view.get_selection() (model, iter) = treeselection.get_selected() if iter: self.remove_button.set_sensitive(True) else: self.remove_button.set_sensitive(False) def execute_button_clicked(self, widget, user=None): # do SNMP stuff, then TFTP stuff, then... widget.set_sensitive(False) if __name__ == '__main__': tftpdialog = TFTPDialog() gtk.main()